1111from enum import Enum
1212from pathlib import Path
1313
14- GW_ROOT_DIR = Path (os .path .dirname (__file__ )).parent
15- GW_CRATE_DIR = GW_ROOT_DIR .joinpath ("rust_bindings" )
16- GW_CONTRACTS_DIR = GW_ROOT_DIR .joinpath ("contracts" )
17- GW_MOCKS_DIR = GW_CONTRACTS_DIR .joinpath ("mocks" )
14+ CI_DIR = Path (os .path .dirname (__file__ ))
15+ REPO_ROOT = CI_DIR .parent
1816
1917# To update forge to the latest version locally, run `foundryup`
2018MIN_FORGE_VERSION = (1 , 3 , 1 )
2119MAX_FORGE_VERSION = (2 , 0 , 0 ) # Exclusive upper bound
2220
2321
22+ class ProjectConfig :
23+ """Configuration for a specific project's bindings."""
24+
25+ def __init__ (self , name : str , root_dir : Path , skip_patterns : list [str ] = None ):
26+ self .name = name
27+ self .root_dir = root_dir
28+ self .crate_dir = root_dir .joinpath ("rust_bindings" )
29+ self .contracts_dir = root_dir .joinpath ("contracts" )
30+ self .skip_patterns = skip_patterns or []
31+
32+ def get_skip_args (self ) -> str :
33+ """Returns forge bind skip arguments for this project."""
34+ return " " .join (f"--skip '{ pattern } '" for pattern in self .skip_patterns )
35+
36+
37+ # Project configurations
38+ PROJECTS = {
39+ "gateway" : ProjectConfig (
40+ name = "Gateway" ,
41+ root_dir = REPO_ROOT .joinpath ("gateway-contracts" ),
42+ skip_patterns = [
43+ "Example" ,
44+ "contracts/mocks/*" ,
45+ ],
46+ ),
47+ "host" : ProjectConfig (
48+ name = "Host" ,
49+ root_dir = REPO_ROOT .joinpath ("host-contracts" ),
50+ skip_patterns = ["fhevm-foundry/*" , "test/*" ],
51+ ),
52+ }
53+
54+
2455def parse_semver (version_str : str ) -> tuple :
2556 """Parses a semver string (e.g., '1.3.1') into a tuple of integers."""
2657 return tuple (int (x ) for x in version_str .split ("." ))
@@ -30,9 +61,17 @@ def init_cli() -> ArgumentParser:
3061 """Inits the CLI of the tool."""
3162 parser = ArgumentParser (
3263 description = (
33- "A tool to check or update the bindings crate of the Gateway contracts."
64+ "A tool to check or update the bindings crate of the Gateway or Host contracts."
3465 )
3566 )
67+
68+ parser .add_argument (
69+ "--project" ,
70+ choices = ["gateway" , "host" ],
71+ required = True ,
72+ help = "The project to check or update bindings for." ,
73+ )
74+
3675 subparsers = parser .add_subparsers (dest = "command" , help = "Subcommands" )
3776
3877 subparsers .add_parser (
@@ -53,7 +92,8 @@ def main():
5392 if args .command not in ["check" , "update" ]:
5493 return cli .print_help ()
5594
56- bindings_updater = BindingsUpdater ()
95+ project_config = PROJECTS [args .project ]
96+ bindings_updater = BindingsUpdater (project_config )
5797
5898 if args .command == "check" :
5999 bindings_updater .check_version ()
@@ -74,21 +114,23 @@ class ExitStatus(Enum):
74114
75115class BindingsUpdater :
76116 """
77- An object used to check if the binding crate of the Gateway contracts is
117+ An object used to check if the binding crate of the contracts is
78118 up-to-date.
79119
80120 Also takes care of updating this crate if requested.
81121 """
82122
83123 tempdir : str
84- gateway_repo_version : str
124+ repo_version : str
125+ config : ProjectConfig
85126
86- def __init__ (self ):
127+ def __init__ (self , config : ProjectConfig ):
128+ self .config = config
87129 self .tempdir = tempfile .mkdtemp ()
88130 BindingsUpdater ._check_forge_installed ()
89- with open (f"{ GW_ROOT_DIR } /package.json" , "r" ) as package_json_fd :
131+ with open (f"{ config . root_dir } /package.json" , "r" ) as package_json_fd :
90132 package_json_content = json .load (package_json_fd )
91- self .gateway_repo_version = package_json_content ["version" ]
133+ self .repo_version = package_json_content ["version" ]
92134
93135 def __del__ (self ):
94136 shutil .rmtree (self .tempdir )
@@ -131,14 +173,15 @@ def _check_forge_installed():
131173 sys .exit (ExitStatus .WRONG_FORGE_VERSION .value )
132174
133175 def check_bindings_up_to_date (self ):
134- """Checks that the Gateway contracts' bindings are up-to-date."""
135- log_info ("Checking that the Gateway contracts' bindings are up-to-date..." )
176+ """Checks that the contracts' bindings are up-to-date."""
177+ log_info (f "Checking that the { self . config . name } contracts' bindings are up-to-date..." )
136178
179+ skip_args = self .config .get_skip_args ()
137180 # We need to include the --no-metadata flag to avoid updating many of the contracts' bytecode
138181 # when only updating one of them (since interfaces are included in many contracts)
139182 return_code = subprocess .call (
140- f"forge bind --root { GW_ROOT_DIR } --module --skip-cargo-toml "
141- f"--hh -b { GW_CRATE_DIR } /src -o { self .tempdir } --skip Example --skip { GW_MOCKS_DIR } /* "
183+ f"forge bind --root { self . config . root_dir } --module --skip-cargo-toml "
184+ f"--hh -b { self . config . crate_dir } /src -o { self .tempdir } { skip_args } "
142185 f"--no-metadata" ,
143186 shell = True ,
144187 stdout = subprocess .DEVNULL ,
@@ -152,28 +195,29 @@ def check_bindings_up_to_date(self):
152195 log_success ("All binding files are up-to-date!" )
153196
154197 def update_bindings (self ):
155- """Updates the Gateway contracts' bindings."""
156- log_info ("Updating Gateway contracts' bindings..." )
198+ """Updates the contracts' bindings."""
199+ log_info (f "Updating { self . config . name } contracts' bindings..." )
157200
201+ skip_args = self .config .get_skip_args ()
158202 # We need to include the --no-metadata flag to avoid updating many of the contracts' bytecode
159203 # when only updating one of them (since interfaces are included in many contracts)
160204 subprocess .run (
161- f"forge bind --root { GW_ROOT_DIR } --hh -b { GW_CRATE_DIR } /src "
162- f"--module --overwrite -o { self .tempdir } --skip Example --skip { GW_MOCKS_DIR } /* "
205+ f"forge bind --root { self . config . root_dir } --hh -b { self . config . crate_dir } /src "
206+ f"--module --overwrite -o { self .tempdir } { skip_args } "
163207 "--no-metadata" ,
164208 shell = True ,
165209 check = True ,
166210 stdout = subprocess .DEVNULL ,
167211 )
168212
169- log_success ("The Gateway contracts' bindings are now up-to-date!" )
213+ log_success (f "The { self . config . name } contracts' bindings are now up-to-date!" )
170214
171215 def check_version (self ):
172216 """
173- Checks that the version of the crate matches the version of the Gateway .
217+ Checks that the version of the crate matches the version of the project .
174218 """
175- log_info ("Checking that the crate's version match the Gateway version..." )
176- with open (f"{ GW_CRATE_DIR } /Cargo.toml" , "r" ) as cargo_toml_fd :
219+ log_info (f "Checking that the crate's version match the { self . config . name } version..." )
220+ with open (f"{ self . config . crate_dir } /Cargo.toml" , "r" ) as cargo_toml_fd :
177221 cargo_toml_content = cargo_toml_fd .read ()
178222
179223 # Find the version in the Cargo.toml
@@ -193,23 +237,23 @@ def check_version(self):
193237 # Extract the version from the matches: the first (and only) captured group from the regex.
194238 cargo_toml_version = matches .group (1 )
195239
196- if self .gateway_repo_version != cargo_toml_version :
240+ if self .repo_version != cargo_toml_version :
197241 log_error (
198- "ERROR: Cargo.toml version does not match Gateway version!\n "
199- f"Gateway version: { self .gateway_repo_version } \n "
242+ f "ERROR: Cargo.toml version does not match { self . config . name } version!\n "
243+ f"{ self . config . name } version: { self .repo_version } \n "
200244 f"Cargo.toml version: { cargo_toml_version } \n "
201245 )
202246 log_info ("Run `make update-bindings` to update the crate's version." )
203247 sys .exit (ExitStatus .CRATE_VERSION_NOT_UP_TO_DATE .value )
204248 log_success (
205- f"The version of the crate match with the Gateway version: { self .gateway_repo_version } !\n "
249+ f"The version of the crate match with the { self . config . name } version: { self .repo_version } !\n "
206250 )
207251
208252 def update_crate_version (self ):
209- """Updates the crate's version to match with the Gateway version."""
253+ """Updates the crate's version to match with the project version."""
210254 log_info ("Updating the crate's version..." )
211255
212- with open (f"{ GW_CRATE_DIR } /Cargo.toml" , "r" ) as cargo_toml_fd :
256+ with open (f"{ self . config . crate_dir } /Cargo.toml" , "r" ) as cargo_toml_fd :
213257 cargo_toml_content = cargo_toml_fd .read ()
214258
215259 # Replace the version in the Cargo.toml
@@ -223,18 +267,18 @@ def update_crate_version(self):
223267 # make sure we do not alter the original format of the Cargo.toml.
224268 cargo_toml_content = re .sub (
225269 r'(\[package\].*?version\s*=\s*")[^"]+(")' ,
226- lambda m : m .group (1 ) + self .gateway_repo_version + m .group (2 ),
270+ lambda m : m .group (1 ) + self .repo_version + m .group (2 ),
227271 cargo_toml_content ,
228272 count = 1 ,
229273 flags = re .DOTALL ,
230274 )
231275
232- with open (f"{ GW_CRATE_DIR } /Cargo.toml" , "w" ) as cargo_toml_fd :
276+ with open (f"{ self . config . crate_dir } /Cargo.toml" , "w" ) as cargo_toml_fd :
233277 cargo_toml_fd .write (cargo_toml_content )
234278
235279 log_success (
236280 f"The crate's version has been successfully updated to "
237- f"{ self .gateway_repo_version } !\n "
281+ f"{ self .repo_version } !\n "
238282 )
239283
240284
0 commit comments