aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/mozart-registry/src/resolver.rs
AgeCommit message (Collapse)Author
2026-05-02fix(resolver): honor root self-provide/replace as require fulfilmentnsfisis
Port Composer's RuleSetGenerator::createRequireRule self-fulfilling branch: when the root composer.json's `provide` or `replace` covers a name it also requires (with intersecting constraints), skip emitting an install-one-of rule for that root require. Composer relies on the root package being a fixed entry in the pool so whatProvides() includes it; Mozart does not yet add the root to the pool, so the same decision is made via explicit `root_provide` / `root_replace` tables threaded through ResolveRequest. Without this, an inline repo package whose name matches the root's provide was being force-installed. Fixes installer fixtures `provider_satisfies_its_own_requirement` and `replacer_satisfies_its_own_requirement`.
2026-05-02feat(resolver): support inline #ref pin and default-branch aliasnsfisis
Adds the missing pieces for installer fixtures that pin a dev package via `dev-foo#hex` or rely on Composer's `default-branch: true` synthetic `9999999-dev` alias. Mirrors Composer at four layers: 1. `mozart_semver::parse_single` strips `dev-...#hex` / `....x-dev#hex` suffixes from constraints (Composer's `parseConstraint` regex). 2. `PackagistVersion` carries `default_branch`. When set on a `dev-` package with no numeric prefix, `packagist_to_pool_inputs` emits the synthetic `9999999-dev` alias — but skips it when an explicit `extra.branch-alias` already covers the version (matches `ArrayLoader::getBranchAlias`). 3. `RuleSetGenerator::generate` picks up `addRulesForRootAliases`: any pool alias whose target was added gets its own alias↔target rules so the SAT solver pulls them in together. 4. `lockfile::generate_lock_file` extracts root `#hex` overrides from `require`/`require-dev` and rewrites source/dist references (and github/gitlab/bitbucket archive URLs) on the matched package, the `setSourceDistReferences` ladder Composer runs in `PoolBuilder`. Resolver also infers `Stability::Dev` from a `dev-foo` style single-atom constraint when no explicit `@flag` is given, mirroring the second loop of `RootPackageLoader::extractStabilityFlags` so the package isn't filtered out under default `stable` minimum-stability. Newly green: install_branch_alias_composer_repo, install_reference, conflict_with_alias_prevents_update_if_not_required, unbounded_conflict_matches_default_branch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02feat(resolver): add branch-alias support across the resolution pipelinensfisis
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>
2026-05-02refactor(registry): plumb RepositorySet and executor through callersnsfisis
ResolveRequest and LockFileGenerationRequest now take Arc<RepositorySet> instead of a raw Cache. install_from_lock now accepts &mut dyn InstallerExecutor instead of constructing FilesystemExecutor internally. Both changes expose the DI injection points needed by the upcoming in-process test harness, where Packagist must be replaced with an empty RepositorySet (Composer's `'packagist' => false` test config) and filesystem install execution must be replaced with a tracing recorder (Composer's InstallationManagerMock). The eager VCS scan and inline-package preload still happen inside resolve(), so the RawRepository array is kept on ResolveRequest as raw_repositories - migrating those through RepositorySet remains a follow-up. RepositorySet gains with_packagist and empty constructors so production callers and future tests have a uniform construction shape. All 136 enabled installer fixtures + 114 mozart-registry tests + 541 mozart lib tests still green; clippy clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02refactor(registry): route Packagist queries through RepositorySetnsfisis
Replace direct packagist::fetch_package_versions calls in resolver::resolve (seed + transitive loops) and lockfile::generate_lock_file with repo_set.load_packages calls. PackagistRepository now propagates errors instead of swallowing them, so the seed loop's strictness and the transitive loop's local-leniency are both preserved exactly. VCS and inline-package repositories are still preloaded directly into the pool builder for now, with their names tracked in skip lists so we don't double-load them through the trait. Migrating them through RepositorySet is a follow-up - vcs_to_pool_inputs and packagist_to_pool_inputs differ in dev-branch handling that needs to be unified first. All 136 enabled installer fixtures + 114 mozart-registry tests + 541 mozart lib tests remain green; clippy clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02feat(resolver): honor --ignore-platform-reqs and wildcard patternsnsfisis
The pool builder and rule-set generator only consulted an exact-match HashSet, so `--ignore-platform-reqs` (no value) and `--ignore-platform-req=ext-foo-*` fell through to the SAT layer and produced "no matching package found" for transitive platform deps. Track the bool flag separately and run each platform name through `mozart_core::matches_wildcard` against the configured patterns. Unblocks four installer fixtures: install-{ignore-platform-package-requirement-wildcard,ignore-platform-package-requirements} update-{ignore-platform-package-requirement-wildcard,ignore-platform-package-requirements}. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01feat(registry): support inline 'type: package' repositoriesnsfisis
Composer's PackageRepository lets composer.json embed full package metadata under repositories[].package, mirroring the on-disk Packagist response shape. The vast majority of installer fixtures under composer/tests/Composer/Test/Fixtures/installer (179 of 189) rely on this — they declare every package they need inline rather than hitting the network. Three pieces wire this into Mozart: 1. mozart-core::package::RawRepository: relax `url` to Option<String> (Composer enforces presence per repo type, not at JSON parse) and add `package: Option<Value>` to receive the inline definition, which can be a single object or an array. 2. mozart-registry::inline_package: a new module that walks `&[RawRepository]`, picks out type=package entries, and reshapes each `package` payload into a PackagistVersion (auto-computing version_normalized when omitted, matching Packagist's output). 3. resolver::resolve and lockfile::generate_lock_file: feed inline packages into the SAT pool builder and short-circuit the Packagist fetch when generating the lock entry for a resolved inline package. The package-name set is shared with the existing VCS-skip logic so the seed and transitive loops don't double-fetch. One additional install-time change: in install_from_lock, packages that have neither dist nor source are now skipped silently instead of bailing with "no dist or source information". This mirrors Composer's MetapackageInstaller (no installer for type=metapackage) and is also what Composer's own AllFunctionalTest exercises via InstallationManagerMock — most inline-package fixtures define synthetic packages with no download metadata, expecting the install operation to be recorded but not actually run. Net effect: installer fixture scoreboard jumps from 7/187 to 103/187. The 84 fixtures still ignored hit issues unrelated to inline-package plumbing — aliases, replace/provide chains, dev-reference handling, allow-list updates, etc. — and are tracked separately. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-02-24fix(solver): iterate live watch chain to prevent infinite loop in propagationnsfisis
The watch graph's propagateLiteral used a cloned chain for iteration while checking the live chain for bounds. After move_watch removed a node, the stale clone kept re-reading the same node index, causing an infinite loop on large dependency sets (e.g. laravel/laravel). Now re-fetch the live chain each iteration, matching Composer's SplDoublyLinkedList semantics where remove() advances the iterator. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24feat(cache): enable repo cache for all Packagist API callsnsfisis
Remove the Option wrapper from repo_cache in ResolveRequest, LockFileGenerationRequest, and fetch_package_versions. All commands now initialize a Cache via build_cache_config(cli.no_cache), ensuring Packagist metadata is cached to disk (respecting --no-cache flag). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23refactor(vcs): convert VcsDriver trait to native asyncnsfisis
Replace manual tokio::runtime::Handle::current().block_on() calls with native async/await throughout all VCS drivers. Introduce AnyVcsDriver enum for static dispatch to avoid dyn trait with async methods. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23feat(vcs): add mozart-vcs crate for VCS repository supportnsfisis
Implement VCS driver/downloader infrastructure mirroring Composer's VCS subsystem. Includes drivers for GitHub, GitLab, Bitbucket, Forgejo, Git, Hg, and SVN with API-based metadata resolution, plus source downloaders for Git/Hg/SVN. Integrates into mozart-registry via vcs_bridge module to scan VCS repositories and feed discovered packages into the SAT resolver. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23fix(update): implement --with constraints, inline shorthand, and APCu ↵nsfisis
passthrough - Parse and apply --with temporary constraints to the resolver - Support inline constraint shorthand (vendor/pkg:1.0.*) - Reject --lock combined with specific package names - Filter magic keywords (lock/nothing/mirrors) from package list - Pass APCu CLI flags through to InstallConfig instead of hardcoding Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22fix(resolver): handle virtual packages and deduplicate pool explorationnsfisis
Virtual/meta packages (e.g. "psr/http-client-implementation") don't exist on Packagist and caused a fatal error during transitive dependency exploration. These packages are resolved via provides/replaces from other packages already in the pool, so 404 errors are now skipped. Also fix PoolBuilder::next_pending() repeatedly returning the same virtual package name by tracking explored names in a HashSet, since virtual packages are never added to inputs and the old check (inputs.any(name)) never matched them. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22fix(platform): inject composer pseudo packages into resolvernsfisis
composer-runtime-api, composer-plugin-api, and composer are Composer pseudo packages that don't exist on Packagist. The resolver was trying to fetch them remotely (HTTP 404) because PackageName::is_platform() didn't recognize them and detect_platform() didn't inject them. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22feat(resolver): replace pubgrub with Composer-ported SAT solvernsfisis
Add mozart-sat-resolver crate implementing a CDCL SAT-based dependency resolver ported from Composer's DependencyResolver. This replaces the pubgrub library to ensure identical resolution behavior with Composer. The new crate includes: pool (package storage with integer IDs), rule/rule_set/rule_set_generator (constraint encoding), decisions (assignment tracking), rule_watch_graph (2-watched literal BCP), solver (CDCL loop with conflict analysis and clause learning), policy (version preference), problem (Composer-style error messages), and transaction (install/update/uninstall operation computation). The registry resolver is rewritten to use PoolBuilder → RuleSetGenerator → Solver pipeline instead of pubgrub's DependencyProvider trait. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22fix(resolver): replace ComposerVersion(u16) with semver::Version(u64)nsfisis
ComposerVersion used u16 segments (max 65535), causing overflow for PHP extensions like ext-dom (version 20031129). The extension became invisible to the resolver, failing create-project with "depends on ext-dom" errors. Use mozart_semver::Version (u64 segments) directly as pubgrub's version type, eliminating the overflow and redundant conversion layer. Also fixes Ord for patch pre-releases (patch1 > stable) and adds Display impl required by pubgrub. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22refactor: reorganize crates to match Composer subpackage structurensfisis
Rename mozart-constraint to mozart-semver (mirrors composer/semver) and extract mozart-class-map-generator from mozart-autoload (mirrors composer/class-map-generator). No logic changes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22fix(resolver): replace __root__ with actual package name in error messagesnsfisis
Composer never shows the internal __root__ identifier to users. Add root_name field to ResolveRequest so the resolver can substitute the real package name (e.g. "laravel/laravel") in pubgrub error reports. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22fix(platform): detect PHP version from runtime instead of hardcoding 8.1nsfisis
PlatformConfig::new() was hardcoded to PHP 8.1 with a fixed extension list, causing resolution failures for packages requiring newer PHP (e.g. Laravel 12 requires >=8.2). Now calls detect_platform() to discover the actual PHP version, extensions and capabilities. Also adds a mutex to composer_home_dir tests to prevent env-var races. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22refactor(async): migrate from blocking HTTP to async/await with tokionsfisis
Replace reqwest::blocking with async reqwest across the entire codebase. All command execute functions, registry API calls (packagist, downloader, resolver, lockfile), and the main entry point now use async/await with the tokio runtime. The pubgrub resolver runs on spawn_blocking since its DependencyProvider trait is synchronous, using Handle::block_on for async I/O within that context. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22refactor(workspace): split monolithic crate into 6 workspace cratesnsfisis
Extract modules from the single `mozart` crate into 5 focused library crates to improve compilation parallelism and architectural clarity: - mozart-constraint: version constraint parser (independent) - mozart-core: base types, console, validation, platform utilities - mozart-archiver: archive creation (tar, zip, bzip2) - mozart-registry: Packagist API, cache, resolver, downloader, lockfile - mozart-autoload: autoloader generation and PHP scanner Refactor Console::from_cli and build_cache_config to accept primitive args instead of &Cli to break circular dependencies. Introduce [workspace.dependencies] for centralized version management. Remove 9 unused direct dependencies from the CLI crate. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>