From 770ba43a6f9657371cacfcd98e0c215d4843bb64 Mon Sep 17 00:00:00 2001 From: Well Date: Tue, 7 Oct 2025 00:37:32 +0800 Subject: [PATCH 1/3] Install soldeer dependencies --- crates/forge/src/args.rs | 4 +-- crates/forge/src/cmd/clone.rs | 8 +++--- crates/forge/src/cmd/init.rs | 6 ++--- crates/forge/src/cmd/install.rs | 43 ++++++++++++++++++++++++++++++--- 4 files changed, 48 insertions(+), 13 deletions(-) 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..e62fb396ba090 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,11 @@ 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 +717,7 @@ 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..e2db9301856dc 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,32 @@ 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>, From 1ef5cfd8b70f69eccf42b770c54419fe09cab7e8 Mon Sep 17 00:00:00 2001 From: Well Date: Tue, 7 Oct 2025 01:03:36 +0800 Subject: [PATCH 2/3] fmt --- crates/forge/src/cmd/clone.rs | 9 +++++++-- crates/forge/src/cmd/install.rs | 8 ++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/crates/forge/src/cmd/clone.rs b/crates/forge/src/cmd/clone.rs index e62fb396ba090..6cf3db9ff7ae9 100644 --- a/crates/forge/src/cmd/clone.rs +++ b/crates/forge/src/cmd/clone.rs @@ -160,7 +160,10 @@ 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) async 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() }; @@ -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()).await.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/install.rs b/crates/forge/src/cmd/install.rs index e2db9301856dc..1f7a7a2510917 100644 --- a/crates/forge/src/cmd/install.rs +++ b/crates/forge/src/cmd/install.rs @@ -294,8 +294,12 @@ async fn install_soldeer_deps_if_needed(dep_path: &Path) -> Result<()> { 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; + 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)?; From ed9247427b3839ada1ebcef1d74f62af591481da Mon Sep 17 00:00:00 2001 From: Well Date: Wed, 8 Oct 2025 00:10:36 +0800 Subject: [PATCH 3/3] make ci happy --- crates/forge/src/args.rs | 2 +- crates/forge/src/cmd/build.rs | 5 +++-- crates/forge/src/cmd/coverage.rs | 3 ++- crates/forge/src/cmd/create.rs | 3 ++- crates/forge/src/cmd/install.rs | 10 ++++------ crates/forge/src/cmd/test/mod.rs | 3 ++- 6 files changed, 14 insertions(+), 12 deletions(-) diff --git a/crates/forge/src/args.rs b/crates/forge/src/args.rs index 6e7f44017ddb3..16a03ef4d3ad0 100644 --- a/crates/forge/src/args.rs +++ b/crates/forge/src/args.rs @@ -73,7 +73,7 @@ pub fn run_command(args: Forge) -> Result<()> { if cmd.is_watch() { global.block_on(watch::watch_build(cmd)) } else { - cmd.run().map(drop) + global.block_on(cmd.run()).map(drop) } } ForgeSubcommand::VerifyContract(args) => global.block_on(args.run()), diff --git a/crates/forge/src/cmd/build.rs b/crates/forge/src/cmd/build.rs index ed87fa68228fe..54bb43a879436 100644 --- a/crates/forge/src/cmd/build.rs +++ b/crates/forge/src/cmd/build.rs @@ -71,10 +71,11 @@ pub struct BuildArgs { } impl BuildArgs { - pub fn run(self) -> Result { + pub async fn run(self) -> Result { let mut config = self.load_config()?; - if install::install_missing_dependencies(&mut config) && config.auto_detect_remappings { + if install::install_missing_dependencies(&mut config).await && config.auto_detect_remappings + { // need to re-configure here to also catch additional remappings config = self.load_config()?; } diff --git a/crates/forge/src/cmd/coverage.rs b/crates/forge/src/cmd/coverage.rs index 420f9dfcfa0bf..4598356a549ee 100644 --- a/crates/forge/src/cmd/coverage.rs +++ b/crates/forge/src/cmd/coverage.rs @@ -83,7 +83,8 @@ impl CoverageArgs { let (mut config, evm_opts) = self.load_config_and_evm_opts()?; // install missing dependencies - if install::install_missing_dependencies(&mut config) && config.auto_detect_remappings { + if install::install_missing_dependencies(&mut config).await && config.auto_detect_remappings + { // need to re-configure here to also catch additional remappings config = self.load_config()?; } diff --git a/crates/forge/src/cmd/create.rs b/crates/forge/src/cmd/create.rs index c72549c9d72ed..61c9d0dba180a 100644 --- a/crates/forge/src/cmd/create.rs +++ b/crates/forge/src/cmd/create.rs @@ -106,7 +106,8 @@ impl CreateArgs { let mut config = self.load_config()?; // Install missing dependencies. - if install::install_missing_dependencies(&mut config) && config.auto_detect_remappings { + if install::install_missing_dependencies(&mut config).await && config.auto_detect_remappings + { // need to re-configure here to also catch additional remappings config = self.load_config()?; } diff --git a/crates/forge/src/cmd/install.rs b/crates/forge/src/cmd/install.rs index 1f7a7a2510917..66fa88155ac4d 100644 --- a/crates/forge/src/cmd/install.rs +++ b/crates/forge/src/cmd/install.rs @@ -93,15 +93,13 @@ impl DependencyInstallOpts { /// See also [`Self::install`]. /// /// Returns true if any dependency was installed. - pub fn install_missing_dependencies(self, config: &mut Config) -> bool { + pub async fn install_missing_dependencies(self, config: &mut Config) -> bool { let lib = config.install_lib_dir(); 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"); - // 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() { + if self.install(config, Vec::new()).await.is_err() { let _ = sh_warn!("Your project has missing dependencies that could not be installed."); } @@ -277,8 +275,8 @@ impl DependencyInstallOpts { } } -pub fn install_missing_dependencies(config: &mut Config) -> bool { - DependencyInstallOpts::default().install_missing_dependencies(config) +pub async fn install_missing_dependencies(config: &mut Config) -> bool { + DependencyInstallOpts::default().install_missing_dependencies(config).await } /// Checks if a dependency has soldeer.lock and installs soldeer dependencies if needed. diff --git a/crates/forge/src/cmd/test/mod.rs b/crates/forge/src/cmd/test/mod.rs index 7c8a47da1bad9..03bb94771f79e 100644 --- a/crates/forge/src/cmd/test/mod.rs +++ b/crates/forge/src/cmd/test/mod.rs @@ -258,7 +258,8 @@ impl TestArgs { let (mut config, evm_opts) = self.load_config_and_evm_opts()?; // Install missing dependencies. - if install::install_missing_dependencies(&mut config) && config.auto_detect_remappings { + if install::install_missing_dependencies(&mut config).await && config.auto_detect_remappings + { // need to re-configure here to also catch additional remappings config = self.load_config()?; }