1010 add_hosts_to_group ,
1111 assign_groups_to_hosts ,
1212 check_path_exists ,
13+ is_valid_regex ,
1314 list_configuration ,
1415 print_dry_run_results ,
1516 print_error ,
@@ -45,6 +46,9 @@ def all(
4546 try :
4647 config = Config ()
4748
49+ if not config .hosts :
50+ print_error ("No hosts" , True )
51+
4852 ssh_client = SSHClient ()
4953 if dry_run :
5054 dry_run_results = ssh_client .begin_dry_run_exec (
@@ -64,6 +68,9 @@ def all(
6468def group (
6569 name : str = typer .Argument (..., help = "Name of the host group to target." ),
6670 cmd : str = typer .Argument (..., help = "The shell command to execute on the group." ),
71+ regex : str = typer .Option (
72+ "" , help = "Filter group members by matching alias with a regex pattern."
73+ ),
6774 timeout : int = typer .Option (
6875 10 , help = "Timeout in seconds for SSH command execution."
6976 ),
@@ -77,12 +84,19 @@ def group(
7784 Args:
7885 name (str): The name of the host group to target.
7986 cmd (str): The shell command to execute remotely.
87+ regex (str): Filter group members by matching alias with regex pattern.
8088 timeout (int): Timeout (in seconds) for both SSH connection and command execution.
8189 dry_run (bool): Show command and host info without executing.
8290 """
8391 try :
92+ if regex and not is_valid_regex (regex ):
93+ print_error ("Invalid regex" , True )
94+
8495 config = Config ()
85- hosts = config .get_hosts_by_group (name )
96+ hosts = config .get_hosts_by_group (name , regex )
97+
98+ if not hosts :
99+ print_error ("Invalid group" , True )
86100
87101 ssh_client = SSHClient ()
88102 if dry_run :
@@ -163,6 +177,9 @@ def push(
163177 ),
164178 all : bool = typer .Option (False , help = "Push to all configured hosts." ),
165179 group : str = typer .Option ("" , help = "Push to a specific group of hosts." ),
180+ regex : str = typer .Option (
181+ "" , help = "Filter group members by matching alias with a regex pattern."
182+ ),
166183 host : str = typer .Option ("" , help = "Push to a single specific host." ),
167184 recurse : bool = typer .Option (
168185 False , help = "Recursively push a directory and its contents."
@@ -182,17 +199,33 @@ def push(
182199 remote_path (str): The destination path on the remote host(s).
183200 all (bool): Push to all hosts.
184201 group (str): Push to a specified group of hosts.
202+ regex (str): Filter group members by matching alias with regex pattern.
185203 host (str): Push to a specified individual host.
186204 recurse (bool): If True, recursively push a directory and all its contents.
187205 dry_run (bool): Show transfer and host info without executing.
188206 """
189- options = [all , bool (group != "" ), bool (host != "" )]
207+ has_all = all
208+ has_group = bool (group != "" )
209+ has_host = bool (host != "" )
210+ has_regex = bool (regex != "" )
211+
212+ if has_regex and not is_valid_regex (regex ):
213+ print_error ("Invalid regex" , True )
214+
215+ options = [has_all , has_group , has_host ]
216+
190217 if sum (options ) != 1 :
191218 print_error (
192219 "You must specify exactly one of --all, --group, or --host." ,
193220 True ,
194221 )
195222
223+ if has_regex and not has_group :
224+ print_error (
225+ "--regex can only be used with --group." ,
226+ True ,
227+ )
228+
196229 if not check_path_exists (local_path ):
197230 print_error (f"Path ({ local_path } ) does not exist" , True )
198231
@@ -205,7 +238,7 @@ def push(
205238 config .configured_hosts ()
206239 if all
207240 else (
208- config .get_hosts_by_group (group )
241+ config .get_hosts_by_group (group , regex )
209242 if group
210243 else [host_obj ]
211244 if host_obj is not None
@@ -241,6 +274,9 @@ def pull(
241274 ),
242275 all : bool = typer .Option (False , "--all" , help = "Pull from all configured hosts." ),
243276 group : str = typer .Option ("" , help = "Pull from a specific group of hosts." ),
277+ regex : str = typer .Option (
278+ "" , help = "Filter group members by matching alias with a regex pattern."
279+ ),
244280 host : str = typer .Option ("" , help = "Pull from a single specific host." ),
245281 recurse : bool = typer .Option (
246282 False , help = "Recursively pull a directory and its contents."
@@ -260,17 +296,33 @@ def pull(
260296 local_path (str): The local file path.
261297 all (bool): Pull from all hosts.
262298 group (str): Pull from a specified group of hosts.
299+ regex (str): Filter group members by matching alias with regex pattern.
263300 host (str): Pull from a specified individual host.
264301 recurse (bool): If True, recursively pull directories and all their contents.
265302 dry_run (bool): Show transfer and host info without executing.
266303 """
267- options = [all , bool (group ), bool (host )]
304+ has_all = all
305+ has_group = bool (group != "" )
306+ has_host = bool (host != "" )
307+ has_regex = bool (regex != "" )
308+
309+ if has_regex and not is_valid_regex (regex ):
310+ print_error ("Invalid regex" , True )
311+
312+ options = [has_all , has_group , has_host ]
313+
268314 if sum (options ) != 1 :
269315 print_error (
270316 "You must specify exactly one of --all, --group, or --host." ,
271317 True ,
272318 )
273319
320+ if has_regex and not has_group :
321+ print_error (
322+ "--regex can only be used with --group." ,
323+ True ,
324+ )
325+
274326 if not check_path_exists (local_path ):
275327 print_error (f"Path ({ local_path } ) does not exist" , True )
276328
@@ -283,7 +335,7 @@ def pull(
283335 config .configured_hosts ()
284336 if all
285337 else (
286- config .get_hosts_by_group (group )
338+ config .get_hosts_by_group (group , regex )
287339 if group
288340 else [host_obj ]
289341 if host_obj is not None
0 commit comments