diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-05-01 21:32:01 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-05-01 21:32:01 +0900 |
| commit | 4453aaddb071515e4b2c263864bd00fe7fa2eee6 (patch) | |
| tree | 1e3473ad0094b9bbae6b14b18e454b8227633cff /crates/mozart | |
| parent | 41655c14ad33f9eed6efcc4490a604a0e2defa4b (diff) | |
| download | php-mozart-4453aaddb071515e4b2c263864bd00fe7fa2eee6.tar.gz php-mozart-4453aaddb071515e4b2c263864bd00fe7fa2eee6.tar.zst php-mozart-4453aaddb071515e4b2c263864bd00fe7fa2eee6.zip | |
feat(install): verify lock file satisfies composer.json requires
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>
Diffstat (limited to 'crates/mozart')
| -rw-r--r-- | crates/mozart/src/commands/install.rs | 25 | ||||
| -rw-r--r-- | crates/mozart/tests/installer.rs | 5 |
2 files changed, 22 insertions, 8 deletions
diff --git a/crates/mozart/src/commands/install.rs b/crates/mozart/src/commands/install.rs index b245182..f1cc6a9 100644 --- a/crates/mozart/src/commands/install.rs +++ b/crates/mozart/src/commands/install.rs @@ -662,7 +662,15 @@ pub async fn execute( } let lock = lockfile::LockFile::read_from_file(&lock_path)?; - // Step 4: Freshness check + // Step 4: Determine dev mode (needed for the lock-vs-composer.json check) + let dev_mode = !args.no_dev; + + // Step 5: Freshness check + lock-vs-composer.json requirement check + // + // Mirrors `Composer\Installer::doInstall()` lines 745-756: if the lock is + // stale, warn; then verify every root require (and require-dev when in dev + // mode) is satisfied by the lock contents. If not, exit with + // ERROR_LOCK_FILE_INVALID (4) before attempting to install. let composer_json_path = working_dir.join("composer.json"); if composer_json_path.exists() { let content = std::fs::read_to_string(&composer_json_path)?; @@ -671,9 +679,20 @@ pub async fn execute( "<warning>Warning: The lock file is not up to date with the latest changes in composer.json. You may be getting outdated dependencies. It is recommended that you run `mozart update`.</warning>" )); } + + let root_pkg = mozart_core::package::read_from_file(&composer_json_path)?; + let missing = lock.get_missing_requirement_info(&root_pkg, dev_mode); + if !missing.is_empty() { + for line in &missing { + console.info(line); + } + return Err(mozart_core::exit_code::bail_silent( + mozart_core::exit_code::LOCK_FILE_INVALID, + )); + } } - // Step 5: Determine if prefer-source is enabled + // Step 6: Determine if prefer-source is enabled let prefer_source = args.prefer_source || args .prefer_install @@ -681,8 +700,6 @@ pub async fn execute( .map(|s| s.eq_ignore_ascii_case("source")) .unwrap_or(false); - // Step 6: Determine dev mode and vendor directory - let dev_mode = !args.no_dev; let vendor_dir = working_dir.join("vendor"); // Step 7: Delegate to shared install_from_lock() diff --git a/crates/mozart/tests/installer.rs b/crates/mozart/tests/installer.rs index 0520108..af38c9e 100644 --- a/crates/mozart/tests/installer.rs +++ b/crates/mozart/tests/installer.rs @@ -297,10 +297,7 @@ installer_fixture!( load_replaced_package_if_replacer_dropped, ignore = "mozart binary cannot yet run this fixture" ); -installer_fixture!( - outdated_lock_file_fails_install, - ignore = "mozart binary cannot yet run this fixture" -); +installer_fixture!(outdated_lock_file_fails_install); installer_fixture!( outdated_lock_file_with_new_platform_reqs_fails, ignore = "mozart binary cannot yet run this fixture" |
