@@ -22,11 +22,25 @@ use foundry_config::{
2222 } ,
2323 filter:: expand_globs,
2424} ;
25- use serde:: Serialize ;
26- use std:: path:: PathBuf ;
25+ use serde:: { Deserialize , Serialize } ;
26+ use std:: { path:: PathBuf , process :: Command } ;
2727
2828foundry_config:: merge_impl_figment_convert!( BuildArgs , build) ;
2929
30+ #[ derive( Debug , Deserialize ) ]
31+ struct SoldeerLockEntry {
32+ name : String ,
33+ version : String ,
34+ source : String ,
35+ #[ serde( default , rename = "checksum" ) ]
36+ _checksum : Option < String > ,
37+ }
38+
39+ #[ derive( Debug , Deserialize ) ]
40+ struct SoldeerLock {
41+ dependencies : Vec < SoldeerLockEntry > ,
42+ }
43+
3044/// CLI arguments for `forge build`.
3145///
3246/// CLI arguments take the highest precedence in the Config/Figment hierarchy.
@@ -80,6 +94,8 @@ impl BuildArgs {
8094 config = self . load_config ( ) ?;
8195 }
8296
97+ self . check_soldeer_lock_consistency ( & config) ;
98+
8399 let project = config. project ( ) ?;
84100
85101 // Collect sources to compile if build subdirectories specified.
@@ -207,6 +223,56 @@ impl BuildArgs {
207223 Ok ( [ config. src , config. test , config. script , foundry_toml] )
208224 } )
209225 }
226+
227+ /// Check soldeer.lock file consistency with actual git revisions
228+ fn check_soldeer_lock_consistency ( & self , config : & Config ) {
229+ let soldeer_lock_path = config. root . join ( "soldeer.lock" ) ;
230+ if !soldeer_lock_path. exists ( ) {
231+ return ;
232+ }
233+
234+ let lock_content = match foundry_common:: fs:: read_to_string ( & soldeer_lock_path) {
235+ Ok ( content) => content,
236+ Err ( _) => return ,
237+ } ;
238+
239+ let soldeer_lock: SoldeerLock = match toml:: from_str ( & lock_content) {
240+ Ok ( lock) => lock,
241+ Err ( _) => return ,
242+ } ;
243+
244+ for dep in & soldeer_lock. dependencies {
245+ if let Some ( ( _, expected_rev) ) = dep. source . split_once ( '#' ) {
246+ let dep_dir_name = format ! ( "{}-{}" , dep. name, dep. version) ;
247+ let dep_path = config. root . join ( "dependencies" ) . join ( & dep_dir_name) ;
248+
249+ if dep_path. exists ( ) {
250+ let actual_rev = Command :: new ( "git" )
251+ . args ( [ "rev-parse" , "HEAD" ] )
252+ . current_dir ( & dep_path)
253+ . output ( ) ;
254+
255+ if let Ok ( output) = actual_rev
256+ && output. status . success ( )
257+ {
258+ let actual_rev = String :: from_utf8_lossy ( & output. stdout ) . trim ( ) . to_string ( ) ;
259+
260+ if !actual_rev. starts_with ( expected_rev)
261+ && !expected_rev. starts_with ( & actual_rev)
262+ {
263+ sh_warn ! (
264+ "Dependency '{}' revision mismatch: \n Expected (from soldeer.lock): {}\n Actual (in {}): {}" ,
265+ dep. name,
266+ expected_rev,
267+ dep_dir_name,
268+ actual_rev
269+ ) . ok ( ) ;
270+ }
271+ }
272+ }
273+ }
274+ }
275+ }
210276}
211277
212278// Make this args a `figment::Provider` so that it can be merged into the `Config`
0 commit comments