| Age | Commit message (Collapse) | Author |
|
Merged mozart-archiver, mozart-autoload, mozart-registry,
mozart-sat-resolver, and mozart-vcs into mozart-core to align
the source layout with Composer's structure.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
|
|
Composer's Locker::getLockedRepository runs each locked package through
ArrayLoader::load, which materializes any extra.branch-alias as a
separate AliasPackage in the locked repository. Mozart was only adding
the base locked package to the pool, so a `dev-master` locked entry
with branch alias `2.2.x-dev` was invisible to numeric root constraints
like `~2.1` on a partial update — the resolver bailed with "no matching
package found" even though Composer accepts the same lock. Surface
each branch-alias as a sibling pool entry pointing at the base via
is_alias_of.
|
|
Composer's VersionParser::normalize maps `master`/`trunk`/`default`
(with or without `dev-` prefix) to `dev-NAME`, not `9999999-dev`. Mozart
was emitting the four-segment 9999999 form for these atoms in
normalize_root_alias_atom, so a root require like `dev-master as 1.1.0`
recorded its target as `9999999.9999999.9999999.9999999-dev` and never
matched the pool's `dev-master` entry — the alias was silently dropped
and transitive `^1.1` requires couldn't see the materialized 1.1.0 alias.
|
|
Partial update of a non-allow-listed dev package now resolves and emits
the locked-repo entry verbatim, mirroring Composer's `PoolBuilder`.
Three coordinated changes:
- resolver: `lock_filter_allows` accepts the locked package's branch-
alias normalized versions, not just the base. Without this, root
constraints like `~2.1` against a `dev-master` locked package whose
branch alias is `2.1.x-dev` failed with "no matching package found".
- lockfile: new `lock_pinned_names` field on `LockFileGenerationRequest`
routes non-allow-listed packages through `previous_lock_lookup`
before `inline_lookup`, so the lock's source/dist references survive
even when the inline metadata has moved to a newer commit.
- update: `apply_partial_update` skips alias entries — re-pinning their
pretty `version` to the base would collapse the alias label and
emit a self-referential entry in the new lock's `aliases[]` block.
Unblocks partial_update_forces_dev_reference_from_lock_for_non_updated_packages.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Mirror Composer's `AliasPackage::replaceSelfVersionDependencies`: a base
package's `replace` / `provide` / `conflict` link whose constraint
matches the base's own version (the resolved form of `self.version`) is
duplicated on the alias at the alias's version. A root require like
`a/aliased: dev-next as 4.1.0-RC2` paired with `replace: { foo:
self.version }` previously left the alias with a `dev-next` constraint,
so a transitive `foo ^4.0` requirement saw no numeric provider and the
solver bailed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Mirror Composer's RootPackageLoader::extractAliases regex so root
requires like `1.*||dev-feature-foo as 1.0.2||^2` and
`dev-feature-foo, dev-feature-foo as 1.0.2` get every `<X> as <Y>`
clause stripped in place and recorded as a separate root alias entry.
The previous single-atom strip left the alias inline, where the parser
then took the RIGHT side per atom and never matched the actual
dev-branch package.
Also fix split_and so a comma-separated AND group like
`dev-foo, dev-bar` splits into two atoms. The space-only operator-glue
heuristic was collapsing it into a single atom because neither half
starts with an operator or digit. Splitting on commas first preserves
the unambiguous separator while keeping `>= 1.0.0` glued within each
comma-part.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Mirror Composer's PoolBuilder::markPackageNameForLoading: when the root
requires a name with a version constraint, loads of that name (seed and
transitive) are filtered down to candidates whose own version (or any
emitted branch-alias version) satisfies the constraint. Without this,
the actual package at a non-matching version slips into the pool
alongside a provider satisfying the root require, masking what should
be a conflict (provider-gets-picked-together-with-other-version-of-
provided-conflict.test).
Also restore the Composer v1 compat path in inline_package: when the
JSON sets version_normalized to the legacy 9999999-dev sentinel,
re-normalize from the human-readable version field so a root require
for `dev-master` matches the loaded package.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Adds a `mozart-php-serialize` crate (a byte-compatible port of PHP's
`serialize()`) and a `mozart-registry::path_repository` module that
expands `type: path` entries into synthetic `type: package`
repositories. Each synthesized package carries the same SHA-1 dist
reference Composer computes (`sha1(\$json . serialize(\$options))`)
so the lockfile and trace lines match Composer byte-for-byte.
Two latent bugs surfaced once the path-repo flow exercised real
resolutions:
- `apply_partial_update` swapped path-repo packages back to their
locked version, defeating Composer's "path repos always reload"
rule (`PoolBuilder` treats them as canonical, not lock-bound).
Mirror the path-repo skip already used when constructing
`locked_packages`.
- `normalize_root_alias_atom` returned the raw input string for
stable numeric atoms (e.g. `1.1.1`), so the alias matcher's
`input.version \!= alias.version_normalized` check — comparing
against pool inputs that carry the 4-segment normalized form —
silently never matched. Run the parsed Version through Display so
both sides are in the same shape.
`install/update::run` gain a `path_repo_base_override: Option<&Path>`
parameter for the in-process test harness: Composer's PHPUnit
`InstallerTest::setUp` does `chdir(__DIR__)` so relative path-repo
URLs resolve against `composer/tests/Composer/Test/`, but the Rust
harness writes `composer.json` into a per-test tempdir and can't
chdir safely under parallel tests. Production callers pass `None`
and resolve against `working_dir`.
Greens 3 ignored installer fixtures:
partial_update_loads_root_aliases_for_path_repos
alias_in_lock
alias_in_lock2
|
|
Mozart silently ignored the `security-advisories` block on inline
`type: package` repositories and the `config.audit.block-insecure`
audit flag, so a `composer update` succeeded with packages a Composer
run would have refused to load. Mirror Composer's
`SecurityAdvisoryPoolFilter` for the slice that feeds the pool:
- Plumb a `security-advisories` field through `RawRepository` and a
`block_insecure` flag through `ResolveRequest`, lifted off
`composer.json`'s `config.audit.block-insecure`.
- Collect every advisory's `affectedVersions` constraint at resolve
time. When `block_insecure` is set and an inline package's
normalized version satisfies the constraint, drop it from the pool
before solving — root requires with no unaffected candidate then
fail with the standard "could not be resolved" error.
|
|
Two related parity gaps surfaced by the `alias-with-reference` fixture:
1. A root require like `dev-main#abcd as 1.0.0` left the SAT-side
constraint as `dev-main#abcd`, which no candidate matched, so
resolution failed before the alias could be materialized. Mirror
Composer's `extractAliases` regex (which captures only the
constraint up to `#`) and `RootPackageLoader::extractReferences`
(which records the hash separately): drop the trailing `#hex` from
the resolver-side constraint and from the alias's left-hand side.
Lockfile generation already pulls the reference back out of the
raw require map for the post-resolve override.
2. `MarkAliasInstalled`'s trace line gated the reference suffix on
the *target* package's stability, so a stable alias like `1.0.0`
pointing at a dev-branch target rendered as `1.0.0 abcd`. Mirror
`AliasPackage::getFullPrettyVersion`: the alias decides on its own
whether to append the suffix based on its own normalized version,
so a stable alias skips the suffix even when the target is dev.
|
|
A partial update reuses every non-allow-listed locked package as a
fixed pool entry, ignoring stability filters. So when a user tightens
`minimum-stability` (or drops a `stability-flags` entry the lock used
to ride on), Mozart silently kept the rejected version and produced a
plan Composer would have failed on. Mirror Composer's
`Pool::isUnacceptableFixedOrLockedPackage` path: walk the locked
packages before pool construction, surface every entry whose version
no longer passes `passes_stability_filter`, and bail with the same
"fixed to <v> (lock file version) ... rejected by your
minimum-stability" pointer Composer prints from
`Problem::getPrettyString`.
|
|
The previous implementation pinned every resolved package back to its
locked version after the resolve, which discarded the new versions the
solver had to pick when a root constraint moved off the lock (e.g. a
require bumped from `1.*` to `2.*`). The lock effectively never moved,
so transitive cascades from a forced root-level update were lost.
Mirror Composer's `Installer::createPolicy(forUpdate=true,
minimalUpdate=true)` instead: thread the lock's `name → normalized
version` map through the policy as `preferred_versions`. The solver now
picks the locked version as a tiebreaker when it still satisfies the
active constraints, but moves freely when a constraint forces a
different version. Drop the post-process hook entirely.
|
|
Two related parity gaps surfaced by the `circular-dependency` fixture:
1. The root's `extra.branch-alias` entry was never materialized in the
pool, and root-level `replace`/`provide`/`conflict` constraints
written as `self.version` were forwarded verbatim. Mirror Composer's
`RootAliasPackage`: resolve `self.version` against the root's
declared version for the base entry, then add an extra alias entry
(carrying the base links plus a duplicate link per `self.version`
original retagged at the alias's version) when the root's version
matches an `extra.branch-alias` key.
2. `Pool::matches_package` returned on the first link to a target name
even when its constraint did not match the query, hiding any later
link to the same target. With the alias above, that masked the
second `replace` link tagged at the alias version. Keep iterating
when target matches but constraint does not, so a later link can
still satisfy.
|
|
Composer's `Transaction::calculateOperations` only emits a
MarkAliasInstalledOperation when the alias isn't already in
`presentAliasMap`. Mirror that here: walk installed.json for each
package being installed/updated, recover its prior alias set (explicit
`extra.branch-alias` entries plus the synthetic `9999999-dev` alias for
`default-branch: true` dev packages), and suppress the trace line when
the new lock's alias normalized version was already there. Avoids the
spurious "Marking ... as installed" emitted on a same-alias dev ref bump.
|
|
Read `config.audit.block-abandoned` from composer.json (defaults to
false) and propagate it to the resolver. When set, the pool builder
skips packages whose `abandoned` field is truthy (`true` or a non-empty
replacement string), matching `SecurityAdvisoryPoolFilter`'s behavior in
`Composer\DependencyResolver`. With no candidates left, a root require
that only matches abandoned versions fails resolution with exit 2.
|
|
Mirror Composer's PackageRepository (extends ArrayRepository) which only
emits packages whose own name matches a queried name. Eager-loading every
inline package let the SAT resolver pick a replacer that nothing required
by name, masking broken transitive dependencies.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Mirror Composer's PoolBuilder/Request semantics for partial updates: each
non-allow-listed locked package becomes a non-fixed pool entry restricted to
its locked version, so `replace`-providing peers cannot silently displace
it. Path-repo packages are exempt — Composer always reloads them from disk.
Threading `--with-dependencies` through `expand_with_direct_dependencies`
now performs transitive expansion with a root-require barrier matching
UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE, so root requires stay
locked when reached via a transitive dep.
Newly green: remove_does_nothing_if_removal_requires_update_of_dep,
update_allow_list_removes_unused, github_issues_4795,
partial_update_with_deps_warns_root.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Mirrors Composer's `RootPackageLoader::extractAliases` +
`PoolBuilder::loadPackage` flow: strip the `as` clause from each root
require so the SAT side sees only the LEFT-hand constraint, and after
every package is loaded run a second pass that materializes an alias
entry for any input matching `(name, version_normalized)`. Locked-only
packages in a partial update are excluded via a new
`ResolveRequest::locked_package_names` so they don't pick up the alias
(`propagateUpdate=false` in Composer).
Two adjacent fixes uncovered while making `install_aliased_alias`
green:
- `Version::cmp` treated unnamed wildcard branches (`1.0.x-dev`,
`is_dev_branch=true && name=None`) as below every numeric version.
They are semantically the same as the four-segment `*-dev` form
Composer's `normalizeBranch` emits, so let only *named* branches
take the shortcut.
- `Constraint::Exact` / `NotEqual` used the derived `==`, which
compared `is_dev_branch` field-by-field and missed the
wildcard/numeric equivalence. Switch to `cmp` so both forms count
as equal.
- `Pool::matches_package` now falls back to parsing `pretty_version`
when the `version` parse doesn't match the constraint, so a
`dev-master` query lines up with a pool entry stored as the
internal `9999999.x.x.x-dev` expansion.
Net effect on installer fixtures: `install_aliased_alias` newly
green, plus `aliased_priority`, `aliased_priority_conflicting`, and
`install_dev_using_dist` come along for the ride.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
The root pool entry now seeded from composer.json carried provides and
replaces but no conflicts, so a root-level conflict like
\`{"some/dep": ">=1.3"}\` was silently dropped. Composer keeps these on
the RootPackage (which lives in the pool via RootPackageRepository), and
the SAT generator turns them into rules that forbid any candidate matching
the constraint — including a branch alias that would resolve to a
matching version. Without that, Mozart cheerfully installs both the
required dev branch and its conflicting alias.
Plumb composer.json's \`conflict\` map through ResolveRequest as
root_conflict and project it onto the root pool entry as PoolLink
conflicts; all callers updated.
Unblocks conflict_on_root_with_alias_prevents_update_if_not_required and
conflict_with_alias_prevents_update installer fixtures.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Composer's RootPackageRepository puts a clone of the root package into
the pool as a fixed entry — its `require` / `require-dev` cleared, but
its name, version, provides, and replaces preserved. That way a
transitive `require` pointing back at the root resolves through the
pool the same way any other reference would, and legal circular
dependencies (root requires A, A requires root) work.
Mozart had no such seed: the rule generator only knew about the root
through the explicit root-require / root-provide / root-replace tables,
so a transitive consumer requiring the root by name failed with no
provider.
Plumb root_version through ResolveRequest (RawPackageData gains a
matching `Option<String>` field), build a fixed PoolPackageInput for
the root with provides/replaces lifted from request.root_provide /
root_replace, and skip the root by name when collecting the resolver's
output so it doesn't leak into the lock file. Falls back to
`1.0.0+no-version-set` (Composer's RootPackage::DEFAULT_PRETTY_VERSION)
when the root composer.json omits `version`.
Unblocks circular_dependency2, conflict_against_replaced_package_problem,
and provider_conflicts installer fixtures.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Adopt indexmap workspace-wide so iteration order is deterministic and
follows insertion order. The non-deterministic order of std HashMap
otherwise leaks into resolver decisions when multiple valid solutions
exist (e.g. cyclic require pairs under prefer-lowest), making behavior
flaky and divergent from Composer's PHP-array semantics.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Mirrors `Composer\Repository\PlatformRepository`'s `$overrides` handling:
each override either replaces a detected platform package version or adds
a virtual one (e.g. ext-dummy), and `false` disables the package.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Mirror Composer's `Solver::checkForRootRequireProblems`: a root require
that resolves to zero pool providers produces no SAT rule, so the
solver previously succeeded with an empty plan instead of reporting
the unresolvable requirement. `RuleSetGenerator::generate` now returns
those misses alongside the rule set, and `resolve()` short-circuits
into `ResolveError::NoSolution` so install/update exit with code 2 to
match Composer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Composer's ArrayLoader and AliasPackage rewrite "self.version" to the
declaring package's own version when building Link objects, so a
package's replace/provide/conflict/require constraints carry a concrete
"= <version>" rather than the literal string. Mozart was passing
"self.version" through verbatim, which then failed to parse in
Pool::matches_package and caused replace_alias.test to fail to find a
provider for c/c 1.* via a/a's branch alias.
Push the substitution into make_pool_links, threading the source
package's normalized version through the call sites in resolver.rs
(packagist/inline/composer-repo) and vcs_bridge.rs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
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`.
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|