From 43efd895d24b7ccd2853fa5bcf08ad0e621f33ce Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sat, 2 May 2026 17:21:29 +0900 Subject: refactor(registry): plumb RepositorySet and executor through callers ResolveRequest and LockFileGenerationRequest now take Arc 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) --- crates/mozart/src/commands/remove.rs | 74 ++++++++++++++++++++++++++---------- 1 file changed, 54 insertions(+), 20 deletions(-) (limited to 'crates/mozart/src/commands/remove.rs') diff --git a/crates/mozart/src/commands/remove.rs b/crates/mozart/src/commands/remove.rs index 7afa51d..8794a10 100644 --- a/crates/mozart/src/commands/remove.rs +++ b/crates/mozart/src/commands/remove.rs @@ -253,9 +253,11 @@ pub async fn execute( platform: PlatformConfig::new(), ignore_platform_reqs: args.ignore_platform_reqs, ignore_platform_req_list: args.ignore_platform_req.clone(), - repo_cache: repo_cache.clone(), + repositories: std::sync::Arc::new( + mozart_registry::repository::RepositorySet::with_packagist(repo_cache.clone()), + ), temporary_constraints: HashMap::new(), - repositories: raw.repositories.clone(), + raw_repositories: raw.repositories.clone(), }; // Print header messages @@ -346,7 +348,9 @@ pub async fn execute( composer_json_content: composer_json_content.clone(), composer_json: raw.clone(), include_dev: dev_mode, - repo_cache: repo_cache.clone(), + repositories: std::sync::Arc::new( + mozart_registry::repository::RepositorySet::with_packagist(repo_cache.clone()), + ), }) .await?; @@ -427,6 +431,10 @@ pub async fn execute( // Install packages (unless --no-install or --dry-run) if !args.no_install && !args.dry_run { + let cache_config = mozart_registry::cache::build_cache_config(cli.no_cache); + let files_cache = mozart_registry::cache::Cache::files(&cache_config); + let mut executor = + mozart_registry::installer_executor::FilesystemExecutor::new(files_cache); super::install::install_from_lock( &new_lock, &working_dir, @@ -447,6 +455,7 @@ pub async fn execute( no_cache: cli.no_cache, }, console, + &mut executor, ) .await?; } @@ -505,9 +514,11 @@ async fn remove_unused( platform: PlatformConfig::new(), ignore_platform_reqs: args.ignore_platform_reqs, ignore_platform_req_list: args.ignore_platform_req.clone(), - repo_cache: repo_cache.clone(), + repositories: std::sync::Arc::new( + mozart_registry::repository::RepositorySet::with_packagist(repo_cache.clone()), + ), temporary_constraints: HashMap::new(), - repositories: raw.repositories.clone(), + raw_repositories: raw.repositories.clone(), }; console.info("Resolving dependencies to detect unused packages..."); @@ -562,7 +573,9 @@ async fn remove_unused( composer_json_content, composer_json: raw.clone(), include_dev: dev_mode, - repo_cache: repo_cache.clone(), + repositories: std::sync::Arc::new( + mozart_registry::repository::RepositorySet::with_packagist(repo_cache.clone()), + ), }) .await?; @@ -572,6 +585,10 @@ async fn remove_unused( // Install if !args.no_install { let vendor_dir = working_dir.join("vendor"); + let cache_config = mozart_registry::cache::build_cache_config(no_cache); + let files_cache = mozart_registry::cache::Cache::files(&cache_config); + let mut executor = + mozart_registry::installer_executor::FilesystemExecutor::new(files_cache); super::install::install_from_lock( &new_lock, working_dir, @@ -592,6 +609,7 @@ async fn remove_unused( no_cache, }, console, + &mut executor, ) .await?; } @@ -838,12 +856,16 @@ mod tests { platform: mozart_registry::resolver::PlatformConfig::new(), ignore_platform_reqs: false, ignore_platform_req_list: vec![], - repo_cache: mozart_registry::cache::Cache::new( - std::env::temp_dir().join("mozart-test-cache"), - false, + repositories: std::sync::Arc::new( + mozart_registry::repository::RepositorySet::with_packagist( + mozart_registry::cache::Cache::new( + std::env::temp_dir().join("mozart-test-cache"), + false, + ), + ), ), temporary_constraints: HashMap::new(), - repositories: vec![], + raw_repositories: vec![], }; let resolved = resolve(&request) .await @@ -853,9 +875,13 @@ mod tests { composer_json_content: content.to_string(), composer_json: raw.clone(), include_dev: false, - repo_cache: mozart_registry::cache::Cache::new( - std::env::temp_dir().join("mozart-test-cache"), - false, + repositories: std::sync::Arc::new( + mozart_registry::repository::RepositorySet::with_packagist( + mozart_registry::cache::Cache::new( + std::env::temp_dir().join("mozart-test-cache"), + false, + ), + ), ), }) .await @@ -881,12 +907,16 @@ mod tests { platform: mozart_registry::resolver::PlatformConfig::new(), ignore_platform_reqs: false, ignore_platform_req_list: vec![], - repo_cache: mozart_registry::cache::Cache::new( - std::env::temp_dir().join("mozart-test-cache"), - false, + repositories: std::sync::Arc::new( + mozart_registry::repository::RepositorySet::with_packagist( + mozart_registry::cache::Cache::new( + std::env::temp_dir().join("mozart-test-cache"), + false, + ), + ), ), temporary_constraints: HashMap::new(), - repositories: vec![], + raw_repositories: vec![], }; let resolved2 = resolve(&request2) .await @@ -898,9 +928,13 @@ mod tests { composer_json_content: composer_json_content2, composer_json: raw, include_dev: false, - repo_cache: mozart_registry::cache::Cache::new( - std::env::temp_dir().join("mozart-test-cache"), - false, + repositories: std::sync::Arc::new( + mozart_registry::repository::RepositorySet::with_packagist( + mozart_registry::cache::Cache::new( + std::env::temp_dir().join("mozart-test-cache"), + false, + ), + ), ), }) .await -- cgit v1.3.1 From 99a33b951502d3e80eb70f53551413b9dc0f4d6c Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sat, 2 May 2026 17:30:27 +0900 Subject: refactor(commands): split install/update into CLI execute + library run MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- crates/mozart/src/commands/create_project.rs | 1 - crates/mozart/src/commands/install.rs | 42 +++++++++++++++++++------- crates/mozart/src/commands/remove.rs | 2 -- crates/mozart/src/commands/require.rs | 1 - crates/mozart/src/commands/update.rs | 45 ++++++++++++++++++---------- 5 files changed, 60 insertions(+), 31 deletions(-) (limited to 'crates/mozart/src/commands/remove.rs') diff --git a/crates/mozart/src/commands/create_project.rs b/crates/mozart/src/commands/create_project.rs index 01b337e..c2c4f92 100644 --- a/crates/mozart/src/commands/create_project.rs +++ b/crates/mozart/src/commands/create_project.rs @@ -522,7 +522,6 @@ pub async fn execute( apcu_autoloader_prefix: None, download_only: false, prefer_source: args.prefer_source, - no_cache: cli.no_cache, }, console, &mut executor, diff --git a/crates/mozart/src/commands/install.rs b/crates/mozart/src/commands/install.rs index 5053783..c8b0431 100644 --- a/crates/mozart/src/commands/install.rs +++ b/crates/mozart/src/commands/install.rs @@ -121,8 +121,6 @@ pub struct InstallConfig { pub download_only: bool, /// Prefer installing from VCS source rather than dist archives. pub prefer_source: bool, - /// Disable the files cache entirely. - pub no_cache: bool, } impl Default for InstallConfig { @@ -140,7 +138,6 @@ impl Default for InstallConfig { apcu_autoloader_prefix: None, download_only: false, prefer_source: false, - no_cache: false, } } } @@ -606,14 +603,37 @@ pub async fn install_from_lock( Ok(()) } +/// CLI entry point. Builds production [`mozart_registry::repository::RepositorySet`] +/// (Packagist) and [`FilesystemExecutor`] from `cli`, then dispatches to [`run`]. pub async fn execute( args: &InstallArgs, cli: &super::Cli, console: &mozart_core::console::Console, ) -> anyhow::Result<()> { - // Step 1: Resolve the working directory + let cache_config = mozart_registry::cache::build_cache_config(cli.no_cache); + let repositories = std::sync::Arc::new( + mozart_registry::repository::RepositorySet::with_packagist( + mozart_registry::cache::Cache::repo(&cache_config), + ), + ); + let mut executor = FilesystemExecutor::new(mozart_registry::cache::Cache::files(&cache_config)); let working_dir = resolve_working_dir(cli); + run(&working_dir, args, console, repositories, &mut executor).await +} +/// Library entry point — pure logic, no `Cli` access. +/// +/// In-process tests construct an empty `RepositorySet` (Composer's +/// `'packagist' => false` test config) and a tracing `InstallerExecutor`, +/// then call this function directly to exercise the install flow without +/// spawning the binary. +pub async fn run( + working_dir: &Path, + args: &InstallArgs, + console: &mozart_core::console::Console, + repositories: std::sync::Arc, + executor: &mut dyn InstallerExecutor, +) -> anyhow::Result<()> { // Step 2: Validate arguments if args.prefer_install.is_some() && (args.prefer_source || args.prefer_dist) { return Err(mozart_core::exit_code::bail( @@ -691,7 +711,10 @@ pub async fn execute( root_reqs: false, bump_after_update: None, }; - return super::update::execute(&update_args, cli, console).await; + // Forward the caller's repositories + executor so in-process tests + // see their mocks honored across the install→update fallback edge. + return super::update::run(working_dir, &update_args, console, repositories, executor) + .await; } let lock = lockfile::LockFile::read_from_file(&lock_path)?; @@ -758,12 +781,10 @@ pub async fn execute( let vendor_dir = working_dir.join("vendor"); // Step 7: Delegate to shared install_from_lock() - let cache_config = mozart_registry::cache::build_cache_config(cli.no_cache); - let files_cache = mozart_registry::cache::Cache::files(&cache_config); - let mut executor = FilesystemExecutor::new(files_cache); + let _ = repositories; // unused — install_from_lock has no resolver phase install_from_lock( &lock, - &working_dir, + working_dir, &vendor_dir, &InstallConfig { dev_mode, @@ -778,10 +799,9 @@ pub async fn execute( apcu_autoloader_prefix: args.apcu_autoloader_prefix.clone(), download_only: args.download_only, prefer_source, - no_cache: cli.no_cache, }, console, - &mut executor, + executor, ) .await } diff --git a/crates/mozart/src/commands/remove.rs b/crates/mozart/src/commands/remove.rs index 8794a10..e499af0 100644 --- a/crates/mozart/src/commands/remove.rs +++ b/crates/mozart/src/commands/remove.rs @@ -452,7 +452,6 @@ pub async fn execute( apcu_autoloader_prefix: args.apcu_autoloader_prefix.clone(), download_only: false, prefer_source: false, - no_cache: cli.no_cache, }, console, &mut executor, @@ -606,7 +605,6 @@ async fn remove_unused( apcu_autoloader_prefix: args.apcu_autoloader_prefix.clone(), download_only: false, prefer_source: false, - no_cache, }, console, &mut executor, diff --git a/crates/mozart/src/commands/require.rs b/crates/mozart/src/commands/require.rs index ead632f..50aa29f 100644 --- a/crates/mozart/src/commands/require.rs +++ b/crates/mozart/src/commands/require.rs @@ -872,7 +872,6 @@ pub async fn execute( apcu_autoloader_prefix: args.apcu_autoloader_prefix.clone(), download_only: false, prefer_source: args.prefer_source, - no_cache: cli.no_cache, }, console, &mut executor, diff --git a/crates/mozart/src/commands/update.rs b/crates/mozart/src/commands/update.rs index fd533fd..8a0bef7 100644 --- a/crates/mozart/src/commands/update.rs +++ b/crates/mozart/src/commands/update.rs @@ -720,17 +720,39 @@ fn major_minor(version: &str) -> (u64, u64) { // Main execute function // ───────────────────────────────────────────────────────────────────────────── +/// CLI entry point. Builds production [`RepositorySet`] (Packagist) and +/// [`FilesystemExecutor`] from `cli`, then dispatches to [`run`]. pub async fn execute( args: &UpdateArgs, cli: &super::Cli, console: &mozart_core::console::Console, ) -> anyhow::Result<()> { let cache_config = mozart_registry::cache::build_cache_config(cli.no_cache); - let repo_cache = mozart_registry::cache::Cache::repo(&cache_config); - - // Step 1: Resolve the working directory + let repositories = std::sync::Arc::new( + mozart_registry::repository::RepositorySet::with_packagist( + mozart_registry::cache::Cache::repo(&cache_config), + ), + ); + let mut executor = mozart_registry::installer_executor::FilesystemExecutor::new( + mozart_registry::cache::Cache::files(&cache_config), + ); let working_dir = super::install::resolve_working_dir(cli); + run(&working_dir, args, console, repositories, &mut executor).await +} +/// Library entry point — pure logic, no CLI / Cli access. +/// +/// In-process tests construct a `RepositorySet` without `PackagistRepository` +/// (Composer's `'packagist' => false` test config) and a tracing +/// `InstallerExecutor`, then call this function directly to exercise the +/// update flow without spawning the binary. +pub async fn run( + working_dir: &std::path::Path, + args: &UpdateArgs, + console: &mozart_core::console::Console, + repositories: std::sync::Arc, + executor: &mut dyn mozart_registry::installer_executor::InstallerExecutor, +) -> anyhow::Result<()> { // Step 2: Handle deprecated flags if args.dev { console.info(&console_format!( @@ -863,9 +885,7 @@ pub async fn execute( platform: PlatformConfig::new(), ignore_platform_reqs: args.ignore_platform_reqs, ignore_platform_req_list: args.ignore_platform_req.clone(), - repositories: std::sync::Arc::new( - mozart_registry::repository::RepositorySet::with_packagist(repo_cache.clone()), - ), + repositories: repositories.clone(), temporary_constraints, raw_repositories: composer_json.repositories.clone(), }; @@ -1023,9 +1043,7 @@ pub async fn execute( composer_json_content: composer_json_content.clone(), composer_json: composer_json.clone(), include_dev: dev_mode, - repositories: std::sync::Arc::new( - mozart_registry::repository::RepositorySet::with_packagist(repo_cache.clone()), - ), + repositories: repositories.clone(), }) .await?; @@ -1222,13 +1240,9 @@ pub async fn execute( .map(|s| s.eq_ignore_ascii_case("source")) .unwrap_or(false); - let cache_config = mozart_registry::cache::build_cache_config(cli.no_cache); - let files_cache = mozart_registry::cache::Cache::files(&cache_config); - let mut executor = - mozart_registry::installer_executor::FilesystemExecutor::new(files_cache); super::install::install_from_lock( &new_lock, - &working_dir, + working_dir, &vendor_dir, &super::install::InstallConfig { dev_mode, @@ -1243,10 +1257,9 @@ pub async fn execute( apcu_autoloader_prefix: args.apcu_autoloader_prefix.clone(), download_only: false, prefer_source, - no_cache: cli.no_cache, }, console, - &mut executor, + executor, ) .await?; } -- cgit v1.3.1