diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-02-22 19:07:10 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-02-22 19:07:10 +0900 |
| commit | 6f512ca9adca91c3ef56b3a760851348d0e95220 (patch) | |
| tree | 4790e660d9d278fce009b94b6712f37115635454 | |
| parent | 33043819de42cbc65d6b81d733c1439536f33688 (diff) | |
| download | php-mozart-6f512ca9adca91c3ef56b3a760851348d0e95220.tar.gz php-mozart-6f512ca9adca91c3ef56b3a760851348d0e95220.tar.zst php-mozart-6f512ca9adca91c3ef56b3a760851348d0e95220.zip | |
feat(validate): implement --with-dependencies flag
Walk vendor/<vendor>/<package>/composer.json files and run the same
manifest validations on each dependency, reporting per-package errors
and warnings with a summary count.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| -rw-r--r-- | crates/mozart/src/commands/validate.rs | 111 |
1 files changed, 109 insertions, 2 deletions
diff --git a/crates/mozart/src/commands/validate.rs b/crates/mozart/src/commands/validate.rs index 6ed4cee..4623b55 100644 --- a/crates/mozart/src/commands/validate.rs +++ b/crates/mozart/src/commands/validate.rs @@ -134,9 +134,14 @@ pub async fn execute( let check_publish = !args.no_check_publish; output_result(&file, &result, check_publish, check_lock, &lock_errors); - // Stub for --with-dependencies + // Validate dependencies' composer.json files if args.with_dependencies { - console.info("The --with-dependencies option is not yet implemented"); + let vendor_dir = file.parent().unwrap_or(Path::new(".")).join("vendor"); + if vendor_dir.exists() { + validate_dependencies(&vendor_dir, args, console); + } else { + console.info("No vendor directory found. Run `mozart install` to install dependencies."); + } } let exit_code = compute_exit_code( @@ -374,6 +379,108 @@ fn check_minimum_stability( } } +// ─── Dependency validation ─────────────────────────────────────────────── + +fn validate_dependencies( + vendor_dir: &Path, + args: &ValidateArgs, + console: &mozart_core::console::Console, +) { + let mut dep_errors = 0u32; + let mut dep_warnings = 0u32; + let mut dep_count = 0u32; + + // Walk vendor/<vendor>/<package>/composer.json + let Ok(vendors) = std::fs::read_dir(vendor_dir) else { + return; + }; + + for vendor_entry in vendors.flatten() { + if !vendor_entry.path().is_dir() { + continue; + } + // Skip non-package dirs (bin, composer, autoload files, etc.) + let vendor_name = vendor_entry.file_name(); + let vendor_str = vendor_name.to_string_lossy(); + if vendor_str.starts_with('.') || vendor_str == "bin" || vendor_str == "composer" { + continue; + } + + let Ok(packages) = std::fs::read_dir(vendor_entry.path()) else { + continue; + }; + + for pkg_entry in packages.flatten() { + if !pkg_entry.path().is_dir() { + continue; + } + + let dep_composer = pkg_entry.path().join("composer.json"); + if !dep_composer.exists() { + continue; + } + + let Ok(content) = std::fs::read_to_string(&dep_composer) else { + continue; + }; + + let Ok(json_value) = serde_json::from_str::<serde_json::Value>(&content) else { + dep_errors += 1; + let pkg_name = format!( + "{}/{}", + vendor_str, + pkg_entry.file_name().to_string_lossy() + ); + eprintln!( + "{}", + mozart_core::console::warning(&format!( + "{pkg_name}: composer.json contains invalid JSON" + )) + ); + continue; + }; + + let mut result = ValidationResult::new(); + validate_manifest(&json_value, args, &mut result); + + dep_count += 1; + + if result.has_errors() || result.has_warnings() { + let pkg_name = format!( + "{}/{}", + vendor_str, + pkg_entry.file_name().to_string_lossy() + ); + + for e in &result.errors { + eprintln!( + "{}", + mozart_core::console::error(&format!("{pkg_name}: {e}")) + ); + dep_errors += 1; + } + for w in &result.warnings { + eprintln!( + "{}", + mozart_core::console::warning(&format!("{pkg_name}: {w}")) + ); + dep_warnings += 1; + } + } + } + } + + if dep_count > 0 { + console.info(&format!( + "Validated {} dependenc{}: {} error(s), {} warning(s)", + dep_count, + if dep_count == 1 { "y" } else { "ies" }, + dep_errors, + dep_warnings + )); + } +} + // ─── Lock file freshness ───────────────────────────────────────────────────── fn check_lock_freshness( |
