diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-05-02 22:21:25 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-05-02 22:21:25 +0900 |
| commit | 8da98493daf5013585e07ec98ca6960a42924edf (patch) | |
| tree | be57603fec29a4bf1e5f546b1ba2e14778595cb3 /crates/mozart-sat-resolver/src/policy.rs | |
| parent | 804b5b9a2a7759af24e41408c82dfc60c6092cf3 (diff) | |
| download | php-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.rs | 46 |
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, } } |
