diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-05-03 21:50:54 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-05-03 21:50:54 +0900 |
| commit | 177b894d7d77a5297bee3b2487ef18a0cae7a596 (patch) | |
| tree | cb526ed3c35414dd6aa8a9bd05c722aa1b7239b6 /crates/mozart/src/commands | |
| parent | b8b81bb9beab64ad073af3b32969566f9ba5a038 (diff) | |
| download | php-mozart-177b894d7d77a5297bee3b2487ef18a0cae7a596.tar.gz php-mozart-177b894d7d77a5297bee3b2487ef18a0cae7a596.tar.zst php-mozart-177b894d7d77a5297bee3b2487ef18a0cae7a596.zip | |
fix(resolver): expand root branch-alias and self.version replace links
Two related parity gaps surfaced by the `circular-dependency` fixture:
1. The root's `extra.branch-alias` entry was never materialized in the
pool, and root-level `replace`/`provide`/`conflict` constraints
written as `self.version` were forwarded verbatim. Mirror Composer's
`RootAliasPackage`: resolve `self.version` against the root's
declared version for the base entry, then add an extra alias entry
(carrying the base links plus a duplicate link per `self.version`
original retagged at the alias's version) when the root's version
matches an `extra.branch-alias` key.
2. `Pool::matches_package` returned on the first link to a target name
even when its constraint did not match the query, hiding any later
link to the same target. With the alias above, that masked the
second `replace` link tagged at the alias version. Keep iterating
when target matches but constraint does not, so a later link can
still satisfy.
Diffstat (limited to 'crates/mozart/src/commands')
| -rw-r--r-- | crates/mozart/src/commands/create_project.rs | 1 | ||||
| -rw-r--r-- | crates/mozart/src/commands/remove.rs | 4 | ||||
| -rw-r--r-- | crates/mozart/src/commands/require.rs | 3 | ||||
| -rw-r--r-- | crates/mozart/src/commands/update.rs | 24 |
4 files changed, 32 insertions, 0 deletions
diff --git a/crates/mozart/src/commands/create_project.rs b/crates/mozart/src/commands/create_project.rs index fc0efee..89b3e4f 100644 --- a/crates/mozart/src/commands/create_project.rs +++ b/crates/mozart/src/commands/create_project.rs @@ -443,6 +443,7 @@ pub async fn execute( locked_package_names: indexmap::IndexSet::new(), locked_packages: Vec::new(), block_abandoned: false, + root_branch_alias: None, }; console.info("Resolving dependencies..."); diff --git a/crates/mozart/src/commands/remove.rs b/crates/mozart/src/commands/remove.rs index eb6ee4a..f41d8b5 100644 --- a/crates/mozart/src/commands/remove.rs +++ b/crates/mozart/src/commands/remove.rs @@ -277,6 +277,7 @@ pub async fn execute( locked_package_names: indexmap::IndexSet::new(), locked_packages: Vec::new(), block_abandoned: false, + root_branch_alias: None, }; // Print header messages @@ -563,6 +564,7 @@ async fn remove_unused( locked_package_names: indexmap::IndexSet::new(), locked_packages: Vec::new(), block_abandoned: false, + root_branch_alias: None, }; console.info("Resolving dependencies to detect unused packages..."); @@ -919,6 +921,7 @@ mod tests { locked_package_names: IndexSet::new(), locked_packages: Vec::new(), block_abandoned: false, + root_branch_alias: None, }; let resolved = resolve(&request) .await @@ -978,6 +981,7 @@ mod tests { locked_package_names: IndexSet::new(), locked_packages: Vec::new(), block_abandoned: false, + root_branch_alias: None, }; let resolved2 = resolve(&request2) .await diff --git a/crates/mozart/src/commands/require.rs b/crates/mozart/src/commands/require.rs index 110bd1a..0816d13 100644 --- a/crates/mozart/src/commands/require.rs +++ b/crates/mozart/src/commands/require.rs @@ -665,6 +665,7 @@ pub async fn execute( locked_package_names: indexmap::IndexSet::new(), locked_packages: Vec::new(), block_abandoned: false, + root_branch_alias: None, }; // Print header messages @@ -1075,6 +1076,7 @@ mod tests { locked_package_names: IndexSet::new(), locked_packages: Vec::new(), block_abandoned: false, + root_branch_alias: None, }; let resolved = resolver::resolve(&request) @@ -1152,6 +1154,7 @@ mod tests { locked_package_names: IndexSet::new(), locked_packages: Vec::new(), block_abandoned: false, + root_branch_alias: None, }; let resolved = resolver::resolve(&request) diff --git a/crates/mozart/src/commands/update.rs b/crates/mozart/src/commands/update.rs index 2853fc8..c4ebdf7 100644 --- a/crates/mozart/src/commands/update.rs +++ b/crates/mozart/src/commands/update.rs @@ -169,6 +169,28 @@ pub struct UpdateChange { /// /// Recognizes "stable", "RC", "beta", "alpha", "dev" (case-insensitive). /// Defaults to `Stability::Stable` for unrecognized values. +/// Resolve the root composer.json's `extra.branch-alias` against the root's +/// `version` field. Returns the alias target (e.g. `"2.0-dev"`) when both +/// `version` and a matching `branch-alias` entry are present, mirroring +/// Composer's `RootPackageLoader` branch-alias detection on the root package. +/// `None` for projects without a `version` or without a matching alias entry. +fn extract_root_branch_alias( + composer_json: &mozart_core::package::RawPackageData, +) -> Option<String> { + let version = composer_json.version.as_deref()?; + if version.is_empty() { + return None; + } + composer_json + .extra_fields + .get("extra") + .and_then(|extra| extra.get("branch-alias")) + .and_then(|aliases| aliases.as_object()) + .and_then(|map| map.get(version)) + .and_then(|v| v.as_str()) + .map(String::from) +} + fn parse_minimum_stability(s: &str) -> Stability { package::Stability::parse(s) } @@ -1136,6 +1158,7 @@ pub async fn run( locked_package_names, locked_packages, block_abandoned, + root_branch_alias: extract_root_branch_alias(&composer_json), }; // Step 6: Print header and run resolver @@ -2274,6 +2297,7 @@ mod tests { locked_package_names: IndexSet::new(), locked_packages: Vec::new(), block_abandoned: false, + root_branch_alias: None, }; let resolved = resolve(&request).await.expect("Resolution should succeed"); |
