diff --git a/Cargo.lock b/Cargo.lock index 27bceb54eb3cd..6b4b435661b2d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4139,6 +4139,7 @@ dependencies = [ "similar-asserts", "solar-compiler", "soldeer-commands", + "soldeer-core", "strum 0.27.2", "svm-rs", "tempfile", diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 2b8143b334cee..13861b920bd4d 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -94,6 +94,7 @@ opener = "0.8" # soldeer soldeer-commands.workspace = true +soldeer-core.workspace = true quick-junit = "0.5.1" [dev-dependencies] diff --git a/crates/forge/src/cmd/build.rs b/crates/forge/src/cmd/build.rs index 54bb43a879436..957aead5a146c 100644 --- a/crates/forge/src/cmd/build.rs +++ b/crates/forge/src/cmd/build.rs @@ -4,7 +4,7 @@ use eyre::{Context, Result}; use forge_lint::{linter::Linter, sol::SolidityLinter}; use foundry_cli::{ opts::BuildOpts, - utils::{LoadConfig, cache_local_signatures}, + utils::{Git, LoadConfig, cache_local_signatures}, }; use foundry_common::{compile::ProjectCompiler, shell}; use foundry_compilers::{ @@ -80,6 +80,9 @@ impl BuildArgs { config = self.load_config()?; } + self.check_soldeer_lock_consistency(&config).await; + self.check_foundry_lock_consistency(&config); + let project = config.project()?; // Collect sources to compile if build subdirectories specified. @@ -207,6 +210,99 @@ impl BuildArgs { Ok([config.src, config.test, config.script, foundry_toml]) }) } + + /// Check soldeer.lock file consistency using soldeer_core APIs + async fn check_soldeer_lock_consistency(&self, config: &Config) { + let soldeer_lock_path = config.root.join("soldeer.lock"); + if !soldeer_lock_path.exists() { + return; + } + + let lockfile = match soldeer_core::lock::read_lockfile(&soldeer_lock_path) { + Ok(lock) => lock, + Err(_) => return, + }; + + let deps_dir = config.root.join("dependencies"); + for entry in &lockfile.entries { + let dep_name = entry.name(); + + // Use soldeer_core's integrity check + match soldeer_core::install::check_dependency_integrity(entry, &deps_dir).await { + Ok(status) => { + use soldeer_core::install::DependencyStatus; + // Check if status indicates a problem + if matches!( + status, + DependencyStatus::Missing | DependencyStatus::FailedIntegrity + ) { + sh_warn!("Dependency '{}' integrity check failed: {:?}", dep_name, status) + .ok(); + } + } + Err(e) => { + sh_warn!("Dependency '{}' integrity check error: {}", dep_name, e).ok(); + } + } + } + } + + /// Check foundry.lock file consistency with git submodules + fn check_foundry_lock_consistency(&self, config: &Config) { + use crate::lockfile::{DepIdentifier, FOUNDRY_LOCK, Lockfile}; + + let foundry_lock_path = config.root.join(FOUNDRY_LOCK); + if !foundry_lock_path.exists() { + return; + } + + let git = match Git::new(&config.root) { + Ok(git) => git, + Err(_) => return, // Skip if not a git repo + }; + + let mut lockfile = Lockfile::new(&config.root).with_git(&git); + if lockfile.read().is_err() { + return; + } + + let deps = lockfile.dependencies(); + + for (dep_path, dep_identifier) in deps { + let full_path = config.root.join(&dep_path); + + if !full_path.exists() { + sh_warn!("Dependency '{}' not found at expected path", dep_path.display()).ok(); + continue; + } + + let actual_rev = match git.get_rev("HEAD", &full_path) { + Ok(rev) => rev, + Err(_) => { + sh_warn!("Failed to get git revision for dependency '{}'", dep_path.display()) + .ok(); + continue; + } + }; + + // Compare with the expected revision from lockfile + let expected_rev = match dep_identifier { + DepIdentifier::Branch { rev, .. } + | DepIdentifier::Tag { rev, .. } + | DepIdentifier::Rev { rev, .. } => rev.clone(), + }; + + if actual_rev != expected_rev { + sh_warn!( + "Dependency '{}' revision mismatch: expected '{}', found '{}'", + dep_path.display(), + expected_rev, + actual_rev + ) + .ok(); + } + } + } } // Make this args a `figment::Provider` so that it can be merged into the `Config`