diff --git a/scripts/modules_management.py b/scripts/modules_management.py index 7cf5b71..85535b4 100644 --- a/scripts/modules_management.py +++ b/scripts/modules_management.py @@ -8,23 +8,25 @@ import select from ..services import config as Config from ..lib import color_log + def set_nonblocking(fd): """Make a file descriptor non-blocking.""" flags = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK) + def run_command(cmd, description): """Run a command and display output in real-time.""" try: color_log.Show("INFO", f"Executing: {description}") - + # Create process without output buffering process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0, # No buffering - universal_newlines=True + universal_newlines=True, ) # Set both pipes to non-blocking mode @@ -63,60 +65,43 @@ def run_command(cmd, description): color_log.Show("FAILED", f"Error executing {description}: {str(e)}") return False -def update_instance(instance_name, action, force_pull=False): - """Update a single instance with git pull, module update, and service restart.""" - color_log.Show("INFO", f"\n=== Starting update process for {instance_name} ===") - - # 1. Pull latest code - color_log.Show("INFO", "Step 1/4: Pulling latest code...") - pull_cmd = ["python", "utility/main.py", "git", "pull", instance_name] - if force_pull: - pull_cmd.append("--force") - if not run_command(pull_cmd, f"Pulling latest code for {instance_name}"): - color_log.Show("WARNING", f"Skipping module update for {instance_name} due to pull failure") - return False +def update_instance(instance_name, action): + """Update a single instance with git pull, module update, and service restart.""" # 2. Update module list - color_log.Show("INFO", "Step 2/4: Updating module list...") - module_list_cmd = ["python", "utility/main.py", "module", "update_list", instance_name] + color_log.Show("INFO", "Updating module list...") + module_list_cmd = [ + "python", + "utility/main.py", + "module", + "update_list", + instance_name, + ] if not run_command(module_list_cmd, f"Updating module list for {instance_name}"): color_log.Show("WARNING", f"Module list update failed for {instance_name}") return False # 3. Update modules - color_log.Show("INFO", "Step 3/4: Updating modules...") + color_log.Show("INFO", "Updating modules...") module_cmd = ["python", "utility/main.py", "module", action, instance_name] if not run_command(module_cmd, f"Updating modules for {instance_name}"): color_log.Show("WARNING", f"Module update failed for {instance_name}") return False - - # 4. Restart service - color_log.Show("INFO", "Step 4/4: Restarting service...") - restart_cmd = ["python", "utility/main.py", "service", "restart", instance_name] - if not run_command(restart_cmd, f"Restarting service for {instance_name}"): - color_log.Show("WARNING", f"Service restart failed for {instance_name}") - return False - - color_log.Show("OK", f"=== Update process completed for {instance_name} ===\n") return True + def main(): # Parse arguments parser = argparse.ArgumentParser(description="Update modules for all instances") parser.add_argument("instance", type=str, help="Instance Name") parser.add_argument( - "action", - help="Action to perform", - choices=["uninstall", "install", "upgrade"] + "action", help="Action to perform", choices=["uninstall", "install", "upgrade"] ) + parser.add_argument("config_path", help="Path to the config file") parser.add_argument( - "config_path", - help="Path to the config file" - ) - parser.add_argument( - "--force-pull", + "--force-pull", action="store_true", - help="Force pull with hard reset (discards local changes)" + help="Force pull with hard reset (discards local changes)", ) args = parser.parse_args() @@ -132,19 +117,64 @@ def main(): # Process each instance success_count = 0 + # Group instances by host and port + instance_groups = {} for instance in instances: if "name" not in instance: color_log.Show("WARNING", f"Instance missing 'name' field. Skipping.") continue - instance_name = instance["name"] - color_log.Show("INFO", f"\nProcessing instance: {instance_name}") - - if update_instance(instance_name, args.action, args.force_pull): - success_count += 1 - color_log.Show("OK", f"Successfully updated {instance_name}") - else: - color_log.Show("FAILED", f"Failed to update {instance_name}") + host = instance.get("host", "unknown") + port = instance.get("port", "unknown") + key = f"{host}:{port}" + + if key not in instance_groups: + instance_groups[key] = [] + instance_groups[key].append(instance) + + # Process instances grouped by host:port + for host_port, group_instances in instance_groups.items(): + host, port = host_port.split(":") + color_log.Show("INFO", f"\nProcessing instances on {host}:{port}") + color_log.Show( + "INFO", f"\n=== Starting update process for {host}:{port} ===" + ) + + # 1. Pull latest code + color_log.Show("INFO", "Pulling latest code...") + pull_cmd = ["python", "utility/main.py", "git", "pull", group_instances[0].get("name","")] + if args.force_pull: + pull_cmd.append("--force") + if not run_command(pull_cmd, f"Pulling latest code for {group_instances[0].get("name","")}"): + color_log.Show( + "WARNING", + f"Skipping module update due to pull failure", + ) + for instance in group_instances: + instance_name = instance["name"] + color_log.Show("INFO", f"Processing instance: {instance_name}") + + if update_instance(instance_name, args.action): + success_count += 1 + color_log.Show("OK", f"Successfully updated {instance_name}") + else: + color_log.Show("FAILED", f"Failed to update {instance_name}") + + # 4. Restart service + color_log.Show("INFO", "Restarting service...") + restart_cmd = [ + "python", + "utility/main.py", + "service", + "restart", + instance_name, + ] + if not run_command(restart_cmd, f"Restarting service for {host}:{port}"): + color_log.Show("WARNING", f"Service restart failed for {host}:{port}") + + color_log.Show( + "OK", f"=== Update process completed for {host}:{port} ===\n" + ) # Summary color_log.Show("INFO", f"\nUpdate Summary:") @@ -157,5 +187,6 @@ def main(): else: color_log.Show("FAILED", f"Failed to update {args.instance}") + if __name__ == "__main__": main()