@@ -30,7 +30,7 @@ def remote_run(self_module, i):
3030 logger = self_module .logger
3131 env = i .get ('env' , {})
3232 remote_host = i .get ('remote_host' , 'localhost' )
33- remote_port = i .get ('remote_port' , 22 )
33+ remote_port = i .get ('remote_port' , '22' )
3434
3535 prune_result = prune_input (
3636 {'input' : i , 'extra_keys_starts_with' : ['remote_' ]})
@@ -40,6 +40,7 @@ def remote_run(self_module, i):
4040 run_input = prune_result ['new_input' ]
4141 mlc_run_cmd = run_input ['mlc_run_cmd' ]
4242
43+ # print(script_cmd)
4344 cur_dir = os .getcwd ()
4445
4546 r = self_module ._select_script (i )
@@ -59,10 +60,20 @@ def remote_run(self_module, i):
5960 if r ['return' ] > 0 :
6061 return r
6162
63+ remote_env = {}
64+
6265 remote_run_settings = r ['remote_run_settings' ]
6366 env = r ['env' ]
67+ state = r ['state' ]
68+ meta = r ['meta' ]
69+
70+ r = call_remote_run_prepare (self_module , meta , script , env , state , i )
71+ if r ['return' ] > 0 :
72+ return r
73+
74+ files_to_copy = r .get ('files_to_copy' , [])
75+ remote_env = r .get ('remote_env' , {})
6476
65- # Execute the experiment script
6677 mlc_script_input = {
6778 'action' : 'run' , 'target' : 'script'
6879 }
@@ -73,23 +84,50 @@ def remote_run(self_module, i):
7384 if i .get ('remote_pull_mlc_repos' , False ):
7485 run_cmds .append ("mlc pull repo" )
7586
76- files_to_copy = []
7787 env_keys_to_copy = remote_run_settings .get ('env_keys_to_copy' , [])
88+ input_mapping = meta .get ('input_mapping' , {})
89+
7890 for key in env_keys_to_copy :
7991 if key in env and os .path .exists (env [key ]):
8092 files_to_copy .append (env [key ])
93+ remote_env [key ] = os .path .join (
94+ "mlc-remote-artifacts" ,
95+ os .path .basename (
96+ env [key ]))
97+
98+ for k , value in input_mapping .items ():
99+ if value == key and k in run_input :
100+ run_input [k ] = remote_env [key ]
101+
102+ i_copy = copy .deepcopy (i )
103+ i_copy ['run_cmd' ] = run_input
104+
105+ r = regenerate_script_cmd (i_copy )
106+ if r ['return' ] > 0 :
107+ return r
108+
109+ # " ".join(mlc_run_cmd.split(" ")[1:])
110+ script_run_cmd = r ['run_cmd_string' ]
111+
112+ if remote_env :
113+ for key in remote_env :
114+ script_run_cmd += f" --env.{ key } ={ remote_env [key ]} "
81115
82- script_run_cmd = " " .join (mlc_run_cmd .split (" " )[1 :])
83- run_cmds .append (f"mlcr { script_run_cmd } " )
116+ run_cmds .append (f"{ script_run_cmd } " )
84117
85118 remote_inputs = {}
119+
86120 for key in ["host" , "port" , "user" , "client_refresh" ,
87- "password" , "skip_host_verify" , "ssh_key_file" ]:
121+ "password" , "skip_host_verify" , "ssh_key_file" , "copy_directory" ]:
88122 if i .get (f"remote_{ key } " ):
89123 remote_inputs [key ] = i [f"remote_{ key } " ]
90124
91125 if files_to_copy :
126+ remote_copy_directory = i .get (
127+ "remote_copy_directory" ,
128+ "mlc-remote-artifacts" )
92129 remote_inputs ['files_to_copy' ] = files_to_copy
130+ remote_inputs ['copy_directory' ] = remote_copy_directory
93131
94132 # Execute the remote command
95133 mlc_remote_input = {
@@ -170,4 +208,175 @@ def update_meta_for_selected_variations(self_module, script, input_params):
170208 # Set Docker-specific configurations
171209 remote_run_settings = state_data ['remote_run' ]
172210 return {'return' : 0 , 'remote_run_settings' : remote_run_settings ,
173- 'env' : env , 'state' : state_data }
211+ 'env' : env , 'state' : state_data , 'meta' : metadata }
212+
213+
214+ def call_remote_run_prepare (self_module , meta , script_item , env , state , i ):
215+
216+ path_to_customize_py = os .path .join (script_item .path , 'customize.py' )
217+ logger = self_module .logger
218+ recursion_spaces = ''
219+
220+ # Check and run remote_run_prepare in customize.py
221+ if os .path .isfile (path_to_customize_py ):
222+ r = utils .load_python_module (
223+ {'path' : script_item .path , 'name' : 'customize' })
224+ if r ['return' ] > 0 :
225+ return r
226+
227+ customize_code = r ['code' ]
228+
229+ customize_common_input = {
230+ 'input' : i ,
231+ 'automation' : self_module ,
232+ 'artifact' : script_item ,
233+ # 'customize': script_item.meta.get('customize', {}),
234+ # 'os_info': os_info,
235+ # 'recursion_spaces': recursion_spaces,
236+ # 'script_tags': script_tags,
237+ # 'variation_tags': variation_tags
238+ }
239+ run_script_input = {}
240+ run_script_input ['customize_code' ] = customize_code
241+ run_script_input ['customize_common_input' ] = customize_common_input
242+
243+ if 'remote_run_prepare' in dir (customize_code ):
244+
245+ logger .debug (
246+ recursion_spaces +
247+ ' - Running remote_run_prepare ...' )
248+
249+ run_script_input ['run_state' ] = {}
250+
251+ ii = copy .deepcopy (customize_common_input )
252+ ii ['env' ] = env
253+ ii ['state' ] = state
254+ ii ['meta' ] = meta
255+ ii ['automation' ] = self_module
256+ # may need to detect versions in multiple paths
257+ ii ['run_script_input' ] = run_script_input
258+
259+ r = customize_code .remote_run_prepare (ii )
260+ return r
261+
262+ return {'return' : 0 }
263+
264+
265+ def regenerate_script_cmd (i ):
266+
267+ remote_run_settings = i .get ('remote_run_settings' , {})
268+ fake_run = i .get ('fake_run' , False )
269+
270+ i_run_cmd = i ['run_cmd' ]
271+
272+ # Remove environment variables with host path values
273+ if 'env' in i_run_cmd :
274+ env = i_run_cmd ['env' ]
275+ for key in list (env ):
276+ value = env [key ]
277+
278+ # Check if the value is a string containing the specified paths
279+ if isinstance (value , str ) and (
280+ os .path .join ("local" , "cache" , "" ) in value or
281+ os .path .join ("MLC" , "repos" , "" ) in value or
282+ "<<<" in value
283+ ):
284+ del env [key ]
285+
286+ # Check if the value is a list and remove matching items
287+ elif isinstance (value , list ):
288+ # Identify values to remove
289+ values_to_remove = [
290+ val for val in value
291+ if isinstance (val , str ) and (
292+ os .path .join ("local" , "cache" , "" ) in val or
293+ os .path .join ("MLC" , "repos" , "" ) in val or
294+ "<<<" in value
295+ )
296+ ]
297+
298+ # Remove key if all values match; otherwise, filter the list
299+ if len (values_to_remove ) == len (value ):
300+ del env [key ]
301+ else :
302+ env [key ] = [
303+ val for val in value if val not in values_to_remove ]
304+
305+ # docker_run_cmd_prefix = i.get('docker_run_cmd_prefix', '')
306+
307+ # Regenerate command from dictionary input
308+ run_cmd = 'mlcr'
309+
310+ skip_input_for_fake_run = remote_run_settings .get (
311+ 'skip_input_for_fake_run' , [])
312+ add_quotes_to_keys = remote_run_settings .get ('add_quotes_to_keys' , [])
313+
314+ def rebuild_flags (
315+ command_dict ,
316+ is_fake_run ,
317+ skip_keys_for_fake_run ,
318+ quote_keys ,
319+ prefix
320+ ):
321+ """
322+ Recursively rebuilds command-line flags from a dictionary of inputs.
323+
324+ :param command_dict: Dictionary containing command-line keys and values.
325+ :param is_fake_run: Boolean indicating if this is a fake run.
326+ :param skip_keys_for_fake_run: List of keys to skip in fake run mode.
327+ :param quote_keys: List of keys that require values to be quoted.
328+ :param prefix: String to prepend to keys for hierarchical keys.
329+ :return: A reconstructed command-line string.
330+ """
331+ command_line = ""
332+
333+ # Sort keys to ensure 'tags' appears first if present.
334+ keys = sorted (command_dict .keys (), key = lambda x : x != "tags" )
335+
336+ for key in keys :
337+ if key in ["input" , "output" , "outdirname" ]:
338+ continue # We have the corresponding env keys in container env string
339+ # Construct the full key with the prefix.
340+ full_key = f"{ prefix } .{ key } " if prefix else key
341+
342+ # Skip keys marked for exclusion in fake run mode.
343+ if is_fake_run and full_key in skip_keys_for_fake_run :
344+ continue
345+
346+ value = command_dict [key ]
347+ quote = '"' if full_key in quote_keys else ""
348+
349+ # Recursively process nested dictionaries.
350+ if isinstance (value , dict ):
351+ command_line += rebuild_flags (
352+ value ,
353+ is_fake_run ,
354+ skip_keys_for_fake_run ,
355+ quote_keys ,
356+ full_key
357+ )
358+ # Process lists by concatenating values with commas.
359+ elif isinstance (value , list ):
360+ list_values = "," .join (
361+ quote_if_needed (
362+ item , quote ) for item in value )
363+ command_line += f" --{ full_key } ,={ list_values } "
364+ # Process scalar values.
365+ else :
366+ if full_key in ['s' , 'v' ]:
367+ command_line += f" -{ full_key } "
368+ else :
369+ command_line += f" --{ full_key } ={ quote_if_needed (value , quote )} "
370+
371+ return command_line
372+
373+ run_cmd += rebuild_flags (i_run_cmd ,
374+ fake_run ,
375+ skip_input_for_fake_run ,
376+ add_quotes_to_keys ,
377+ '' )
378+
379+ # run_cmd = docker_run_cmd_prefix + ' && ' + \
380+ # run_cmd if docker_run_cmd_prefix != '' else run_cmd
381+
382+ return {'return' : 0 , 'run_cmd_string' : run_cmd }
0 commit comments