aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/mozart-registry/src
AgeCommit message (Collapse)Author
2026-05-02feat(installer): add trace recorder and topo install ordernsfisis
Adds TraceRecorderExecutor (Composer's InstallationManagerMock analog), which records every install/update/uninstall as a string matching Composer's *Operation::__toString output (after strip_tags) - the load-bearing assertion target for in-process fixture tests. Two changes were needed to make the recorder useful: - InstallerExecutor::uninstall_package gains a version parameter, and install_from_lock now looks up both the uninstall and the Update-from-version from installed.json. Previously the Update path passed the new version as a placeholder; the recorder needs the real old version to emit `Upgrading pkg (old => new)`. - compute_operations now topologically sorts the lock contents (deps before dependents) before computing actions, mirroring Composer's Transaction::calculateOperations. Without this, packages would install in alphabetical order and the trace would diverge from Composer's expectation. Also adds crates/mozart/tests/installer_in_process.rs with the in-process harness scaffold: parses the same .test fixtures, builds a tempdir, calls commands::install::run / update::run with an empty RepositorySet (no Packagist) and a TraceRecorderExecutor, then asserts exit code + EXPECT trace. One fixture wired up: suggest_replaced - the original CI failure that motivated this whole DI refactor. It now passes on the in-process path because the empty RepositorySet makes b/b unreachable just like Composer's `'packagist' => false` test config, and the resolver finds c/c (which replaces b/b) via the inline package repo's eager preload. Step F will migrate every fixture currently in installer.rs to the new harness; remaining divergences (alias handling, output ordering, replace trace shape, etc.) will surface as individual follow-ups. All 136 existing spawn-based 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): 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-02refactor(registry): introduce Repository and InstallerExecutor traitsnsfisis
Sets up DI scaffolding for in-process installer E2E tests, mirroring how Composer's PHPUnit suite swaps Packagist (FactoryMock) and the install manager (InstallationManagerMock) without touching the network or filesystem. Additions: - Repository trait + RepositorySet (Composer's RepositoryInterface analog), with PackagistRepository, InlinePackageRepository, VcsRepository impls. - InstallerExecutor trait (Composer's InstallationManager analog) with FilesystemExecutor extracted from install_from_lock. install_from_lock now delegates per-package install/uninstall verbs to FilesystemExecutor; console output orchestration stays in the caller so existing --EXPECT-OUTPUT-shape assertions remain comparable. No behavior change - all 136 enabled installer fixtures still pass. Also tightens the installer_fixture\! ignore form to a single token (installer_fixture\!(name, ignore)) for readability. 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-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-05-01feat(registry): accept v1 (bare array) installed.jsonnsfisis
Composer's FilesystemRepository::initialize branches on isset($data['packages']) — object form is v2, bare array is v1 — and treats dev-package-names/dev as optional. Mirror that in InstalledPackages::read so Mozart consumes shared .test fixtures (which use v1) without harness preprocessing, and so installs over v1-era vendor directories keep working. Drop the v1→v2 wrapper that was added to mozart-test-harness for the same reason. Removes #[ignore] from update_to_empty_from_locked (2/187 green). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-02-24fix(cache): enable dist archive caching for all commandsnsfisis
files_cache was Option<&Cache> and install_from_lock always passed None, so downloaded zip/tar archives were never cached. Make the parameter non-optional (&Cache) and wire it through every command that downloads dist archives (install, update, require, remove, create-project, archive). The Cache internally respects --no-cache via its enabled flag, so the Option wrapper was unnecessary. Co-Authored-By: Claude Opus 4.6 <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-23feat(tracing): instrument network requests with tracing spansnsfisis
Add #[tracing::instrument] and debug logs to all HTTP-calling functions in mozart-registry and mozart-vcs for request-level observability (URL, status code, cache hits, download size). 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-22feat(search): display abandoned package indicator in search resultsnsfisis
Parse the `abandoned` field from Packagist search API responses and show a "! Abandoned !" warning inline, matching Composer's behavior. 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(registry): handle Stability::Dev in version constraint buildernsfisis
Replace unreachable\!() with proper @dev suffix output. Although Dev is normally handled by an early return, this prevents a panic if the control flow is ever refactored. 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-22chore: cargo fmtnsfisis
2026-02-22refactor(http): centralize User-Agent string in mozart-corensfisis
Add mozart_core::http::user_agent() that returns a consistent "Mozart/<version> (<os>; <arch>)" string. Replace all scattered user-agent definitions across mozart-registry and mozart CLI commands. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22feat(metadata-minifier): add mozart-metadata-minifier cratensfisis
Port composer/metadata-minifier to Rust as an independent workspace crate. Implements expand() and minify() for Packagist's delta-encoded version metadata. Update mozart-registry to use the new crate for transparent minified response handling, and add __unset sentinel support to PackagistVersion deserialization. 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>