aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/mozart-sat-resolver/src/policy.rs
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-02 22:21:25 +0900
committernsfisis <nsfisis@gmail.com>2026-05-02 22:21:25 +0900
commit8da98493daf5013585e07ec98ca6960a42924edf (patch)
treebe57603fec29a4bf1e5f546b1ba2e14778595cb3 /crates/mozart-sat-resolver/src/policy.rs
parent804b5b9a2a7759af24e41408c82dfc60c6092cf3 (diff)
downloadphp-mozart-8da98493daf5013585e07ec98ca6960a42924edf.tar.gz
php-mozart-8da98493daf5013585e07ec98ca6960a42924edf.tar.zst
php-mozart-8da98493daf5013585e07ec98ca6960a42924edf.zip
feat(resolver): add branch-alias support across the resolution pipeline
Plumb Composer's `extra.branch-alias` mechanism end-to-end so a dev branch (e.g. `dev-foobar`) can be installed alongside its numeric alias (e.g. `3.2.x-dev`) and resolve constraints written against the alias target. Concretely: - `mozart-semver`: stop treating pure-numeric `-dev` as a wildcard branch — `3.2.9999999.9999999-dev` (the form `normalizeBranch` emits) now parses as a classical version with `is_dev_branch=false`, so constraints like `3.2.*` match it. - `mozart-registry/composer_repo`: load `type: composer` repositories from `file://` URLs (legacy embedded `packages.json`). - `mozart-registry/resolver`: emit pool entries in pairs for dev branches with `extra.branch-alias`, link them via `is_alias_of`, and apply `@dev`/`@beta` etc. stability suffix flags from root requires. - `mozart-sat-resolver`: alias rules (`PackageAlias` / `PackageInverseAlias`) so alias and target install together; alias packages skipped from same-name conflict indexing. - `mozart-sat-resolver/policy`: `DefaultPolicy` now honors `prefer_stable` via Composer's stability-tier comparison. - `mozart-registry/lockfile`: split resolved set into real packages vs. alias entries; populate the `aliases[]` block. - `mozart-registry/installer_executor`: new `MarkAliasInstalled` operation; `format_full_pretty_version` mirroring `BasePackage::getFullPrettyVersion` (appends source ref[0..7] for dev/git packages). - Test harness rewrites fixture-relative `file://` URLs to absolute paths. Newly green fixtures: `install_branch_alias_composer_repo`, `alias_solver_problems`, `alias_solver_problems2`, `conflict_with_all_dependencies_option_dont_recommend_to_use_it`, `unbounded_conflict_does_not_match_default_branch_with_branch_alias`, `unbounded_conflict_does_not_match_default_branch_with_numeric_branch`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/mozart-sat-resolver/src/policy.rs')
-rw-r--r--crates/mozart-sat-resolver/src/policy.rs46
1 files changed, 45 insertions, 1 deletions
diff --git a/crates/mozart-sat-resolver/src/policy.rs b/crates/mozart-sat-resolver/src/policy.rs
index aa63be7..a66719f 100644
--- a/crates/mozart-sat-resolver/src/policy.rs
+++ b/crates/mozart-sat-resolver/src/policy.rs
@@ -64,8 +64,20 @@ impl DefaultPolicy {
let pkg_a = pool.literal_to_package(a);
let pkg_b = pool.literal_to_package(b);
- // If same name, prefer higher version (or lower if prefer_lowest)
+ // If same name, apply Composer's policy ordering. Mirrors
+ // `DefaultPolicy::versionCompare`: when `prefer_stable` is on and
+ // the two candidates have different stabilities, the more-stable
+ // one wins outright — `prefer_lowest` only kicks in within the same
+ // stability tier. Otherwise sort by version (asc for prefer_lowest,
+ // desc otherwise).
if pkg_a.name == pkg_b.name {
+ if self.prefer_stable {
+ let stab_a = stability_priority(&pkg_a.version);
+ let stab_b = stability_priority(&pkg_b.version);
+ if stab_a != stab_b {
+ return stab_a.cmp(&stab_b);
+ }
+ }
let cmp = self.compare_versions(&pkg_a.version, &pkg_b.version);
return if self.prefer_lowest {
cmp
@@ -111,6 +123,37 @@ impl Default for DefaultPolicy {
}
}
+/// Map a normalized version string to Composer's stability priority
+/// (`BasePackage::STABILITIES`). Lower = more stable. Stable=0, RC=5, beta=10,
+/// alpha=15, dev=20. Mirrors `DefaultPolicy::versionCompare`'s comparison
+/// when `prefer_stable` is set.
+fn stability_priority(version: &str) -> u8 {
+ let Ok(v) = mozart_semver::Version::parse(version) else {
+ return 0;
+ };
+ if v.is_dev_branch {
+ return 20;
+ }
+ match v.pre_release.as_deref() {
+ None => 0,
+ Some(pre) => {
+ let lower = pre.to_lowercase();
+ if lower.starts_with("dev") {
+ 20
+ } else if lower.starts_with("alpha") || lower == "a" {
+ 15
+ } else if lower.starts_with("beta") || lower == "b" {
+ 10
+ } else if lower.starts_with("rc") {
+ 5
+ } else {
+ // patch/pl/p / unknown → stable
+ 0
+ }
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -126,6 +169,7 @@ mod tests {
provides: vec![],
conflicts: vec![],
is_fixed: false,
+ is_alias_of: None,
}
}