From 8da98493daf5013585e07ec98ca6960a42924edf Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sat, 2 May 2026 22:21:25 +0900 Subject: feat(resolver): add branch-alias support across the resolution pipeline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .../mozart-sat-resolver/src/rule_set_generator.rs | 36 +++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) (limited to 'crates/mozart-sat-resolver/src/rule_set_generator.rs') diff --git a/crates/mozart-sat-resolver/src/rule_set_generator.rs b/crates/mozart-sat-resolver/src/rule_set_generator.rs index 83570d5..b5dfcdb 100644 --- a/crates/mozart-sat-resolver/src/rule_set_generator.rs +++ b/crates/mozart-sat-resolver/src/rule_set_generator.rs @@ -128,6 +128,37 @@ impl<'a> RuleSetGenerator<'a> { let conflict_names: Vec = pkg.conflict_names().into_iter().map(String::from).collect(); let requires = pkg.requires.clone(); + let alias_target = pkg.is_alias_of; + + if let Some(target_id) = alias_target { + // Mirror Composer's RuleSetGenerator::addRulesForPackage alias + // branch: enqueue the target, emit `(-alias | target)` so the + // alias forces the target, and `(-target | alias)` so the + // target forces the alias (they install together). The alias + // is NOT indexed under its name for same-name conflicts — + // Composer skips that for aliases too. + work_queue.push_back(target_id); + + let alias_rule = Rule::two_literals( + -(current_id as Literal), + target_id as Literal, + RuleReason::PackageAlias, + ReasonData::AliasPackage(current_id), + ); + self.rules.add(alias_rule, RuleType::Package); + + let inverse_rule = Rule::two_literals( + -(target_id as Literal), + current_id as Literal, + RuleReason::PackageInverseAlias, + ReasonData::AliasPackage(current_id), + ); + self.rules.add(inverse_rule, RuleType::Package); + + // The aliased target carries the actual requires; skip + // alias's own (link-rewritten copy) to avoid duplicates. + continue; + } // Index by every name this package fully claims (own name + // `replace` targets). Same-name conflict rules (below) then @@ -135,7 +166,8 @@ impl<'a> RuleSetGenerator<'a> { // identity. Mirrors `BasePackage::getNames(false)` indexing in // Composer's RuleSetGenerator::addRulesForPackage — `provide` // targets are intentionally omitted so that providers can - // coexist with the package they provide. + // coexist with the package they provide. Alias packages are + // skipped because the target package's name already covers them. for name in conflict_names { self.added_packages_by_name .entry(name) @@ -270,6 +302,7 @@ mod tests { provides: vec![], conflicts: vec![], is_fixed: false, + is_alias_of: None, } } @@ -313,6 +346,7 @@ mod tests { provides: vec![], conflicts: vec![], is_fixed: false, + is_alias_of: None, }, make_input("b/b", "1.0.0.0"), ], -- cgit v1.3.1