| Age | Commit message (Collapse) | Author |
|
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>
|
|
Two pieces of Composer's update-trace machinery were missing:
1. VersionParser::isUpgrade in Composer\Package\Version (which overrides
the upstream Semver one) substitutes dev-master / dev-trunk /
dev-default with the 9999999-dev default-branch alias, then returns
true whenever either side starts with `dev-`. Mozart's is_upgrade
compared via the generic version order, so dev-master → dev-foo came
out as Downgrading. Port the override.
2. Transaction::calculateOperations seeds removeAliasMap from the
currently-installed AliasPackages and emits MarkAliasUninstalled for
every entry not covered by the new lock. Mozart never emitted those,
so updating away from a branch-aliased package produced no trace
line for the alias retirement. Walk installed.json's
`extra.branch-alias` map, compare against the new lock's aliases[]
block, and emit a MarkAliasUninstalled PackageOperation (a new
variant on the executor surface — no filesystem effects, only the
trace recorder cares).
Unblocks update_alias, update_alias_lock2, and update_no_dev_still_resolves_dev
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>
|
|
Composer's UpdateOperation::format renders the from/to versions through
DISPLAY_SOURCE_REF_IF_DEV first, but if both sides come out identical it
re-renders in DISPLAY_SOURCE_REF (when source refs differ) or
DISPLAY_DIST_REF (when only dist refs differ) so the trace doesn't show
a useless `pkg (X => X)` line. Mozart skipped the switch and emitted the
default form on both halves, so a same-version-different-dist-ref update
showed up as `dev-master def000 => dev-master def000` instead of
`dev-master def000 => dev-master`.
Add format_update_pretty_versions to render the pair Composer's way and
plumb the resolved to_full_pretty through PackageOperation::Update so
the trace recorder uses it verbatim.
Unblocks update_installed_reference and update_picks_up_change_of_vcs_type
installer fixtures.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Composer's Transaction fires an UpdateOperation when an installed
package's source/dist reference moved, even if the version string is
unchanged — that is how a `dev-main#abcd` root require pinning a new
commit propagates through `composer install`. Mozart was checking only
(name, version) and short-circuiting to Skip, so the package stayed
pinned to whatever reference installed.json carried.
Compare references in compute_operations and route mismatches into
Action::Update. The trace recorder needs the from-side display string
to include the reference suffix (`dev-master abc123`) so the EXPECT
output matches Composer's UpdateOperation::format; thread that through
PackageOperation::Update as a separate from_full_pretty field while
keeping from_version (sans suffix) for the upgrade-vs-downgrade
direction check, which has to compare normalized versions like
Composer's VersionParser::isUpgrade does.
Unblocks update_reference, update_reference_picks_latest, and
updating_dev_updates_url_and_reference installer fixtures.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Mozart's install-order topological sort marked every package required by
any other as non-root, so cycle members all fell out of the root set and
the cycle fallback emitted them in input (alphabetical) order. Composer
instead walks the sorted result map and removes each package's required
providers as it goes, skipping outer packages already removed — leaving
the highest-sort-key cycle member as a root and giving DFS a deterministic
entry point. Mirror that.
Unblocks the prefer_lowest_branches installer fixture.
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>
|
|
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`.
|
|
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>
|
|
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>
|
|
Replaces the spawn-based runner in tests/installer.rs with the
in-process harness from Step E. Every fixture now goes through
mozart::commands::{install,update}::run with an empty RepositorySet
(Composer's `'packagist' => false` test config) and a
TraceRecorderExecutor (Composer's InstallationManagerMock), and the
EXPECT section is now asserted against the recorder's trace -
load-bearing for behavior parity, not just exit-code.
The original CI failure (suggest_replaced) is now legitimately tested:
the empty RepositorySet makes b/b unreachable just like Composer's
test config, the inline package repo's eager preload finds c/c which
replaces b/b, and the topological install order in compute_operations
produces the c/c -> a/a trace the fixture pins.
Strict trace assertion surfaced 60 Mozart-vs-Composer divergences that
the exit-code-only spawn runner had been silently ignoring. Each is
marked `installer_fixture\!(name, ignore)` for now; the categories
break down roughly as:
- alias handling (alias_in_lock2, install_aliased_alias, update_alias*)
- replace / provider trace shape (replace_priorities,
provider_satisfies_its_own_requirement, replacer_*)
- update direction strings (update_changes_url, update_reference,
update_dev_*)
- partial-update + lock interactions (partial_update_*)
- allow-list with replace/dependency interactions
(update_allow_list_with_dependencies_require_new*)
These each become individual follow-up Mozart bugs rather than mass
silent-pass.
Also marks prefer_lowest_branches as ignore: it's a real flake driven
by HashSet iteration order in the resolver, where two equivalent
candidates can be picked in either order. That's a separate
determinism bug worth its own fix.
The proxy-hack env-vars in mozart-test-harness::runner are removed -
no test currently spawns the binary, and the in-process harness
expresses Packagist disablement directly via RepositorySet::empty
rather than relying on TCP failure to suppress network calls.
Headline numbers: 75 passed (in-process, exit-code + EXPECT trace) +
112 ignored, vs prior 136 passed (spawn, exit-code only) + 51 ignored.
The drop in passing count reflects the stricter assertion bar, not
new regressions.
Also removes tests/installer_in_process.rs - its single proof-of-
concept fixture (suggest_replaced) is now part of the unified
installer.rs harness.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
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>
|
|
Carve commands::install::execute and commands::update::execute into thin
CLI-arg-driven wrappers + run() entry points that take (working_dir,
args, console, repositories, executor) directly. The wrappers build a
production RepositorySet (Packagist) + FilesystemExecutor from cli, then
dispatch to run; in-process tests will call run directly with an empty
RepositorySet (Composer's `'packagist' => false` test config) and a
tracing InstallerExecutor.
The install -> update fallback (no composer.lock present) now goes
through update::run, forwarding the caller's repositories + executor so
test mocks survive the edge.
Also drop the now-dead InstallConfig::no_cache field — install_from_lock
stopped consuming the cache when FilesystemExecutor was extracted in the
earlier DI plumbing pass, so the field has no effect.
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>
|
|
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>
|
|
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>
|
|
`apply_partial_update` and `apply_patch_only` both pinned non-listed
packages back to the lock by copying `LockedPackage.version_normalized`
verbatim, falling back to the raw pretty `version` when the field was
missing. Lock files written by Composer always include the field, but
hand-written fixtures (every `--LOCK--` block in the installer
fixtures, in particular) typically only carry `version`. The 3-segment
form ("1.0.0") then leaked into the resolved package, where
`LockFileGenerationRequest::inline_lookup` compares against the
4-segment normalizer output ("1.0.0.0") and missed inline `type:
package` entries — triggering a Packagist fetch (and proxy-blocked
failure under the test harness) for a package that should never need
one.
Extract a single `locked_version_normalized` helper that runs the
pretty version through `mozart_semver::Version::parse(...).to_string()`
when the lock omits `version_normalized`, and use it from both call
sites. Mirrors `packagist_to_pool_inputs` and `inline_lookup`, which
already produce the 4-segment form.
Unblocks 26 installer fixtures: the entire update-allow-list cluster
(16, minus the alias subcase), five partial-update cases, and five
others (full-update-minimal-changes, load-replaced-package-if-replacer-dropped,
remove-deletes-unused-deps, remove-does-nothing-if-removal-requires-update-of-dep,
update-changes-url). Scoreboard: 107 → 133 of 187 installer fixtures.
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>
|
|
Composer's InstalledFilesystemRepository::write() dumps the full
package via ArrayDumper, so flags like `abandoned` and
`default-branch` (which Mozart parks in LockedPackage::extra_fields)
should round-trip from composer.lock into vendor/composer/installed.json.
locked_to_installed_entry was zeroing the destination's extra_fields,
silently stripping these flags every time installed.json got
rewritten.
Carry the extra_fields map across verbatim. The
install-forces-reinstall-if-abandon-changes installer fixture is
already exit-0 green at the harness layer; this aligns the actual
end-state with Composer's EXPECT-INSTALLED so a future EXPECT-INSTALLED
comparison won't re-flag this gap.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Mirrors Composer\Package\Loader\RootPackageLoader::load(): if the root
package's "name" appears as a key in its own "require" or "require-dev"
map, fail loudly before reaching the resolver. Without this, Mozart
would silently let the request hit Packagist (which has no entry for
the root's vendor/name) and report a misleading "could not be found"
error.
Wired into install::execute (when a lock file is present) and
update::execute (the no-lock fallback path). Carries the same wording
as Composer's RuntimeException so a future EXPECT-OUTPUT comparison
will match.
Also extends the installer test harness: when a fixture sets
EXPECT-EXCEPTION but no EXPECT-EXIT-CODE, assert that Mozart exits
non-zero. Full exception-class matching remains a follow-up (see
.ken/test_design.md §7.2).
Closes the gap exercised by the install-self-from-root installer
fixture.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Mirrors Composer's platform-requirements check during
Installer::doInstall(): merge platform requires from the lock's
platform/platform-dev fields with the root composer.json
require/require-dev (composer.json wins on duplicate keys), then
verify them against the detected runtime platform. If any are missing
or unsatisfied, print the standard "Your lock file does not contain a
compatible set of packages" message followed by Problem 1..N entries
and exit with DEPENDENCY_RESOLUTION_FAILED (2) instead of silently
proceeding to "Nothing to install" with exit 0. Closes the gap
exercised by the outdated-lock-file-with-new-platform-reqs-fails
installer fixture.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
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>
|
|
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>
|
|
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>
|
|
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 direct println\!/eprintln\! calls with console.write(),
console.info(), and console.write_stdout() across all command
handlers to respect verbosity settings.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
- Migrate eprintln\! to Console for consistent colored output
- Use Composer terminology in lock file operations: Locking instead of
Installing, Upgrading/Downgrading instead of Updating
- Add is_downgrade() helper to distinguish upgrades from downgrades
- Pass Console through install_from_lock for proper output handling
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
Improves testability and ensures proper resource cleanup by returning
errors through the existing MozartError/exit_code mechanism instead of
terminating the process directly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
Sync descriptions with upstream Composer. Replace product name
references (Composer/composer) with Mozart.
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>
|
|
- Detect scripts-descriptions and scripts-aliases keys referencing
non-existent scripts and emit warnings matching Composer's behavior
- Respect config.lock=false in composer.json to skip lock file checks
unless --check-lock is explicitly passed
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
- clean-backups now preserves the most recent backup for rollback
- Rollback no longer deletes the backup file after restoring
- Show version and channel in update/already-up-to-date messages
- Print rollback suggestion after successful update
- Show version instead of file path in rollback output
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
Composer stores repositories as {"name": {"type":...}} while Mozart
only understood [{"name":"name","type":...}]. This adds normalization
so Mozart can read both formats for list, get-url, set-url, and remove.
Also distinguishes "no URL" from "not found" in get-url errors.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
- Use installed packages by default instead of always preferring lock file
- Error on unknown needle package instead of misleading "can be installed"
- Return exit code 1 when prohibitors are found
- Deduplicate output rows in dependency table
- Print resolution hint suggesting require/update --dry-run
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>
|
|
- Reject invalid option combinations: --direct with --all/--platform/--available,
--tree with --all/--available/--latest/--path, --self with package argument
- Reject unsupported --format values (only "text" and "json" allowed)
- Warn when --ignore is used without --outdated
- Support wildcard patterns in --ignore values via matches_wildcard
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
- Revert composer.json and composer.lock to original content on resolution failure
- Detect and remove packages from opposite section to prevent duplicates
- Block self-require (requiring the root package itself)
- Read sort-packages, optimize-autoloader, classmap-authoritative, and
apcu-autoloader from composer.json config as defaults
- Pass APCu CLI flags through to InstallConfig instead of hardcoding
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
Composer
- Extract matches_wildcard to mozart-core for reuse across commands
- Support wildcard patterns in --package and --ignore arguments
- Use ^<installed_version> for semver-safe classification instead of root constraint
- Replace std::process::exit(1) with bail_silent for proper cleanup
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
Match Composer's help output by adding long_about with a description
and link to https://getcomposer.org/doc/03-cli.md#suggests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
Switch from blocklist to whitelist for script event blocking, detect
all namespaced PHP callbacks (not just *Command), use bail_silent for
exit codes, remove redundant COMPOSER_DEV_MODE env_overrides, handle
--timeout 0 as no timeout, and respect --no-scripts flag.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
COMPOSER_DEV_MODE
Emit per-pattern warnings for unmatched package names, read
optimize-autoloader/classmap-authoritative/apcu-autoloader from
composer.json config section, and set COMPOSER_DEV_MODE env var.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
Match Composer's Symfony Table formatting: add Name/Version/Licenses
header row to text output and use bordered ASCII table for summary.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
- Add COMPOSER_DEFAULT_VENDOR env var support for package name default
- Swap USERNAME/USER check order to match Composer (matters on Windows)
- Add COMPOSER_DEFAULT_AUTHOR/COMPOSER_DEFAULT_EMAIL env var support
- Require both name and email for author (Composer behavior)
- Use bail_silent for abort to prevent double error message
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
Style vendor names with <comment> (yellow) and package names with
<info> (green) to match Composer's Symfony Console output. Route all
output through Console::write_stdout() so --quiet is respected.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
Replace std::process::exit() with bail_silent() so Rust cleanup runs
properly. Style the binary listing output with console_format\! markup
to match Composer's Symfony Console styling.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
Replace std::process::exit() with bail_silent() so Rust cleanup and
Drop handlers run properly on non-zero exit. Add a check for git
color.ui=always which is known to cause issues with git output parsing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
- Extend self.version replacement to conflict, provide, and replace
link types (previously only require and require-dev)
- Only rewrite self.version when VCS metadata is actually removed,
matching Composer's behavior
- Read optimize-autoloader, classmap-authoritative, and apcu-autoloader
from the project's composer.json config section instead of hardcoding
false
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
Composer auto-detects the shell from the $SHELL environment variable
when the shell argument is not provided. Mozart previously required
the argument and errored without it.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
Match Composer's ClearCacheCommand behavior:
- Print per-directory status messages (clearing/GC) instead of a single summary
- Skip read-only caches with an informational message
- Print message for non-existent cache directories instead of silently skipping
- Catch filesystem errors and always return exit code 0
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|