| Age | Commit message (Collapse) | Author |
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
apcu-prefix implicit enable
- Restrict --prefer-install to source/dist/auto and --audit-format to
table/plain/json/summary via clap value_parser
- Error when --prefer-install is combined with --prefer-source/--prefer-dist
- Wire --download-only through InstallConfig to skip autoloader and installed.json
- Implicitly enable --apcu-autoloader when --apcu-autoloader-prefix is set
- Apply same validation fixes to update, require, remove, create-project commands
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
Read optimize-autoloader, classmap-authoritative, apcu-autoloader from
composer.json config section. Reject --dev with --no-dev and --strict-psr/
--strict-ambiguous without --optimize. Emit pre/post generation messages
with class count in optimized mode. Track ambiguous class mappings and
exit with code 2 when --strict-ambiguous detects conflicts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
Introduce a Symfony Console-style tag macro that replaces verbose
patterns like `console::info(&format!("text {name}"))` with
`console_format!("<info>text {name}</info>")`. Supports all 6 tag
types (info, comment, error, question, highlight, warning) with
format argument distribution across multiple tagged segments.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
Match Composer behavior: instead of failing with an error when no
composer.lock is present, show a warning and delegate to the update
command to resolve dependencies from composer.json.
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>
|
|
Implement Phase 7.2 error handling & UX infrastructure:
- Add exit_code module with MozartError, bail()/bail_silent() helpers,
and Composer-compatible exit code constants (0-5, 100)
- Redesign Console struct with Verbosity enum (Quiet/Normal/Verbose/
VeryVerbose/Debug), ANSI auto-detection via IsTerminal, and
verbosity-gated output methods (info/verbose/debug/error)
- Thread Console through all 33 command execute() signatures
- Replace all std::process::exit() calls with structured MozartError
returns handled in main()
- Migrate eprintln\! status messages to console.info() for quiet-mode
suppression
- Add suggest module with Levenshtein distance and "Did you mean?"
formatting for future package name suggestions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
Implement a cache module with CacheConfig and Cache structs supporting
read/write (string and binary), atomic writes via temp+rename, TTL-based
expiration, and size-limited garbage collection. Wire the repo cache into
packagist.rs and resolver.rs for API response caching, and the files
cache into downloader.rs for dist archive caching. Implement the
clear-cache command with full clear and --gc modes. All existing call
sites pass None for backward compatibility.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
strict-psr
Add PHP file scanner (php_scanner.rs) with class/interface/trait/enum
detection, comment/string/heredoc stripping, and PSR-4/PSR-0 validation.
Extend autoload generation with: classmap directory scanning, --optimize
mode (PSR-4/PSR-0 to classmap), --classmap-authoritative, --apcu caching
with optional prefix, platform_check.php generation, and --strict-psr
violation reporting. Wire new options through dump-autoload, install,
require, update, and remove commands.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
Replace positional boolean parameters in install_from_lock with a
structured InstallConfig. Add platform requirement warnings, download
progress display, classmap-authoritative autoloader support, and
prefer-source detection across install/update/require/remove commands.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
dependency logic
Add the `depends` (why) and `prohibits` (why-not) commands that query
the dependency graph to answer "which packages require X?" and "which
packages prevent version Y of X from being installed?" respectively.
Introduces the shared `dependency` module with package loading from
lock file or installed.json, forward/inverted dependency graph walking,
recursive traversal with cycle detection, and table/tree output formatters.
Adds `conflict` field to LockedPackage for conflict-based prohibition
detection, updating all struct literals across install, remove, require,
and update test helpers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
Add full update command supporting --lock (content-hash refresh only),
--dry-run, --no-install, --no-dev, --prefer-stable, --prefer-lowest,
and partial updates (named packages). Extract install_from_lock() from
install.rs for shared use. Add Stability::parse() to package.rs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
Implement autoloader generation that produces all standard Composer
autoloader files (autoload.php, autoload_real.php, autoload_static.php,
autoload_psr4.php, autoload_namespaces.php, autoload_classmap.php,
autoload_files.php, installed.php) plus embeds ClassLoader.php,
InstalledVersions.php, and LICENSE from the Composer submodule. Wire
into the install command (gated by --no-autoloader) and replace the
todo!() in dump-autoload with a working implementation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
Replaces the todo!() stub with a full implementation that reads
composer.lock, computes install/update/skip/remove operations by
comparing against vendor/composer/installed.json, downloads packages
via the downloader module, and writes the updated installed registry.
Handles edge cases: missing lock file, stale lock file, no dist info,
empty packages, --dry-run, --no-dev, deprecated flags, and vendor
directory cleanup after removals.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
|
|
|