aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/mozart-registry/src/lockfile.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-02fix(installer): match Composer's transaction order and uninstall labelnsfisis
Three coupled changes that bring `compute_operations` + the in-process trace recorder into byte-parity with Composer's `Transaction::__toString` output: - `TraceRecorderExecutor`: emit "Removing X (V)" instead of "Uninstalling X (V)" — Composer's `UninstallOperation::__toString` uses "Removing". - `install_from_lock`: run removals before installs/updates to mirror `Transaction::moveUninstallsToFront`. Both dry-run and real-execution branches now emit the same prefix order. - `topological_sort`: replace recursive DFS with the stack-based DFS that Composer uses in `Transaction::calculateOperations`. Roots are seeded reverse-alphabetically (matching `setResultPackageMaps`'s uasort with `strcmp(b, a)`), and `getProvidersInResult` is mirrored by treating a package's `provide`/`replace` keys as additional name targets when resolving a `require` link. To make the third change work end-to-end, `LockedPackage` gains typed `provide` and `replace` fields (Composer's lock preserves them; Mozart was silently dropping them). `packagist_version_to_locked_package` now copies them through. Unignores 13 installer fixtures (10 newly green from the fix, 3 that were already green-but-still-flagged): conflict_downgrade_nested, install_from_lock_removes_package, install_security_advisory_matching_dependency, load_replaced_package_if_replacer_dropped, partial_update_keeps_older_dep_* (×2), partial_update_security_advisory_matching_locked_dep, provider_packages_can_be_installed_together_with_provided_if_both_installable, remove_deletes_unused_deps, replace_priorities, update_allow_list_require_new_replace, update_allow_list_with_dependencies_require_new_replace, update_requiring_decision_reverts_and_learning_positive_literals. Installer scoreboard: 75/187 → 88/187. 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-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-05-01fix(registry): accept composer.lock without content-hashnsfisis
Composer's `Locker` treats `content-hash` as optional with BC support (see Locker::isLocked() / isFresh() lines 142-147): if a lock predates the field — or, in the case of installer fixtures, deliberately omits it — Composer simply considers the lock "not fresh" against any composer.json. Mozart's deserializer was strict, rejecting the lock with `missing field content-hash` before any of the install-time checks could run. Default the field to empty via `#[serde(default)]`. With an empty hash, `is_fresh()` returns false (matching Composer's BC behavior, so the freshness warning still fires) and downstream code that overwrites `content_hash` continues to work unchanged. Closes the parsing barrier exercised by the updating-dev-from-lock-removes-old-deps installer fixture. Note: matching Composer's exact operations trace ("Upgrading a/devpackage …", alias-removal lines) requires a `compute_operations` that compares package source references — out of scope for this change and tracked in .ken/test_design.md §7.2 under "EXPECT (operations trace) 比較". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01feat(install): verify lock file satisfies composer.json requiresnsfisis
Mirrors Composer's Installer::doInstall() check: before installing from an existing composer.lock, walk every root require (and require-dev in dev mode) and confirm the lock contains a satisfying package. If any are missing or fail the constraint, print the standard bullet-list diagnostic and exit with LOCK_FILE_INVALID (4) instead of blindly attempting to install and failing later with a misleading "no dist or source information" error. Closes the gap exercised by the outdated-lock-file-fails-install installer fixture. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01refactor: fix clippy warningsnsfisis
Replace if-let/else-return with `?`, swap `as_ref().map(|k| k.as_slice())` for `as_deref()`, and switch test fixtures from `vec\![]` to array literals where ownership is unneeded. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01fix(registry): default missing composer.lock _readme to canonical textnsfisis
Composer treats _readme as write-only metadata: Locker.php injects it on write but the loader never requires it. Mozart's deserializer was strict, so any composer.lock without _readme failed to parse — including most fixtures under composer/tests/.../installer/*.test. Co-Authored-By: Claude Opus 4.7 (1M context) <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-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): 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-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>