diff --git a/crates/forge/src/args.rs b/crates/forge/src/args.rs index f7a6d39d646e1..6e7f44017ddb3 100644 --- a/crates/forge/src/args.rs +++ b/crates/forge/src/args.rs @@ -86,10 +86,10 @@ pub fn run_command(args: Forge) -> Result<()> { }, ForgeSubcommand::Create(cmd) => global.block_on(cmd.run()), ForgeSubcommand::Update(cmd) => cmd.run(), - ForgeSubcommand::Install(cmd) => cmd.run(), + ForgeSubcommand::Install(cmd) => global.block_on(cmd.run()), ForgeSubcommand::Remove(cmd) => cmd.run(), ForgeSubcommand::Remappings(cmd) => cmd.run(), - ForgeSubcommand::Init(cmd) => cmd.run(), + ForgeSubcommand::Init(cmd) => global.block_on(cmd.run()), ForgeSubcommand::Completions { shell } => { generate(shell, &mut Forge::command(), "forge", &mut std::io::stdout()); Ok(()) diff --git a/crates/forge/src/cmd/clone.rs b/crates/forge/src/cmd/clone.rs index 7c5002128b648..6cf3db9ff7ae9 100644 --- a/crates/forge/src/cmd/clone.rs +++ b/crates/forge/src/cmd/clone.rs @@ -110,7 +110,7 @@ impl CloneArgs { let meta = Self::collect_metadata_from_client(address, &client).await?; // step 2. initialize an empty project - Self::init_an_empty_project(&root, install)?; + Self::init_an_empty_project(&root, install).await?; // canonicalize the root path // note that at this point, the root directory must have been created let root = dunce::canonicalize(&root)?; @@ -160,11 +160,14 @@ impl CloneArgs { /// * `root` - the root directory of the project. /// * `enable_git` - whether to enable git for the project. /// * `quiet` - whether to print messages. - pub(crate) fn init_an_empty_project(root: &Path, install: DependencyInstallOpts) -> Result<()> { + pub(crate) async fn init_an_empty_project( + root: &Path, + install: DependencyInstallOpts, + ) -> Result<()> { // Initialize the project with empty set to true to avoid creating example contracts let init_args = InitArgs { root: root.to_path_buf(), install, empty: true, ..Default::default() }; - init_args.run().map_err(|e| eyre::eyre!("Project init error: {:?}", e))?; + init_args.run().await.map_err(|e| eyre::eyre!("Project init error: {:?}", e))?; Ok(()) } @@ -717,7 +720,9 @@ mod tests { let mut project_root = tempfile::tempdir().unwrap().path().to_path_buf(); let client = mock_etherscan(address); let meta = CloneArgs::collect_metadata_from_client(address, &client).await.unwrap(); - CloneArgs::init_an_empty_project(&project_root, DependencyInstallOpts::default()).unwrap(); + CloneArgs::init_an_empty_project(&project_root, DependencyInstallOpts::default()) + .await + .unwrap(); project_root = dunce::canonicalize(&project_root).unwrap(); CloneArgs::parse_metadata(&meta, Chain::mainnet(), &project_root, false, false) .await diff --git a/crates/forge/src/cmd/init.rs b/crates/forge/src/cmd/init.rs index ad550cbc7209c..8cea85496c58d 100644 --- a/crates/forge/src/cmd/init.rs +++ b/crates/forge/src/cmd/init.rs @@ -55,7 +55,7 @@ pub struct InitArgs { } impl InitArgs { - pub fn run(self) -> Result<()> { + pub async fn run(self) -> Result<()> { let Self { root, template, @@ -215,10 +215,10 @@ impl InitArgs { if !offline { if root.join("lib/forge-std").exists() { sh_warn!("\"lib/forge-std\" already exists, skipping install...")?; - self.install.install(&mut config, vec![])?; + self.install.install(&mut config, vec![]).await?; } else { let dep = "https://github.com/foundry-rs/forge-std".parse()?; - self.install.install(&mut config, vec![dep])?; + self.install.install(&mut config, vec![dep]).await?; } } diff --git a/crates/forge/src/cmd/install.rs b/crates/forge/src/cmd/install.rs index 4501a4aff8607..1f7a7a2510917 100644 --- a/crates/forge/src/cmd/install.rs +++ b/crates/forge/src/cmd/install.rs @@ -9,6 +9,7 @@ use foundry_common::fs; use foundry_config::{Config, impl_figment_convert_basic}; use regex::Regex; use semver::Version; +use soldeer_commands::{Command, Verbosity, commands::install::Install}; use std::{ io::IsTerminal, path::{Path, PathBuf}, @@ -59,9 +60,9 @@ pub struct InstallArgs { impl_figment_convert_basic!(InstallArgs); impl InstallArgs { - pub fn run(self) -> Result<()> { + pub async fn run(self) -> Result<()> { let mut config = self.load_config()?; - self.opts.install(&mut config, self.dependencies) + self.opts.install(&mut config, self.dependencies).await } } @@ -97,7 +98,10 @@ impl DependencyInstallOpts { if self.git(config).has_missing_dependencies(Some(lib)).unwrap_or(false) { // The extra newline is needed, otherwise the compiler output will overwrite the message let _ = sh_println!("Missing dependencies found. Installing now...\n"); - if self.install(config, Vec::new()).is_err() { + + // Create a runtime to handle async install + let rt = tokio::runtime::Runtime::new().unwrap(); + if rt.block_on(self.install(config, Vec::new())).is_err() { let _ = sh_warn!("Your project has missing dependencies that could not be installed."); } @@ -108,7 +112,7 @@ impl DependencyInstallOpts { } /// Installs all dependencies - pub fn install(self, config: &mut Config, dependencies: Vec) -> Result<()> { + pub async fn install(self, config: &mut Config, dependencies: Vec) -> Result<()> { let Self { no_git, commit, .. } = self; let git = self.git(config); @@ -256,6 +260,11 @@ impl DependencyInstallOpts { } } sh_println!("{msg}")?; + + // Check if the dependency has soldeer.lock and install soldeer dependencies + if let Err(e) = install_soldeer_deps_if_needed(&path).await { + sh_warn!("Failed to install soldeer dependencies for {}: {e}", dep.name)?; + } } // update `libs` in config if not included yet @@ -272,6 +281,36 @@ pub fn install_missing_dependencies(config: &mut Config) -> bool { DependencyInstallOpts::default().install_missing_dependencies(config) } +/// Checks if a dependency has soldeer.lock and installs soldeer dependencies if needed. +async fn install_soldeer_deps_if_needed(dep_path: &Path) -> Result<()> { + let soldeer_lock = dep_path.join("soldeer.lock"); + + if soldeer_lock.exists() { + sh_println!(" Found soldeer.lock, installing soldeer dependencies...")?; + + // Change to the dependency directory and run soldeer install + let original_dir = std::env::current_dir()?; + std::env::set_current_dir(dep_path)?; + + let result = soldeer_commands::run( + Command::Install(Install::default()), + Verbosity::new( + foundry_common::shell::verbosity(), + if foundry_common::shell::is_quiet() { 1 } else { 0 }, + ), + ) + .await; + + // Change back to original directory + std::env::set_current_dir(original_dir)?; + + result.map_err(|e| eyre::eyre!("Failed to run soldeer install: {e}"))?; + sh_println!(" Soldeer dependencies installed successfully")?; + } + + Ok(()) +} + #[derive(Clone, Copy, Debug)] struct Installer<'a> { git: Git<'a>,