├── .gitignore ├── DockerGen.py ├── README.md ├── img └── foto.png └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | .qodo 2 | -------------------------------------------------------------------------------- /DockerGen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import subprocess 3 | import sys 4 | import os 5 | 6 | def check_dependencies(): 7 | required_packages = ["rich", "yaml"] 8 | missing_packages = [] 9 | 10 | for package in required_packages: 11 | try: 12 | __import__(package) 13 | except ImportError: 14 | missing_packages.append(package) 15 | 16 | if missing_packages: 17 | print("\n[ERROR] Faltan dependencias necesarias:") 18 | for pkg in missing_packages: 19 | print(f" - {pkg}") 20 | print("\nPor favor, instala las dependencias con:") 21 | print(" pip install -r requirements.txt\n") 22 | sys.exit(1) # Terminar ejecución si faltan paquetes 23 | 24 | 25 | # Verificar dependencias antes de continuar 26 | check_dependencies() 27 | 28 | # Importar rich y otras librerías 29 | import yaml 30 | from rich.console import Console 31 | from rich.prompt import Prompt, Confirm 32 | from rich.panel import Panel 33 | from rich.table import Table 34 | 35 | console = Console() 36 | 37 | def print_banner(): 38 | """ Muestra el banner de DockerGen""" 39 | banner = r""" 40 | ____ _ ____ 41 | | _ \ ___ ___| | _____ _ __ / ___| ___ _ __ 42 | | | | |/ _ \ / __| |/ / _ \ '__| | _ / _ \ '_ \ 43 | | |_| | (_) | (__| < __/ | | |_| | __/ | | | 44 | |____/ \___/ \___|_|\_\___|_| \____|\___|_| |_| 45 | 46 | ---- By: MARH ------------------------------------ 47 | """ 48 | console.print(Panel(banner, border_style="", expand=False)) 49 | 50 | def get_non_empty_input(prompt_message: str) -> str: 51 | """ Solicita al usuario una entrada que no esté vacía. """ 52 | while True: 53 | value = console.input(f"[bold green]{prompt_message}[/bold green] ").strip() 54 | if value: 55 | return value 56 | console.print("[red]Este campo es obligatorio. Intenta nuevamente.[/red]") 57 | 58 | def input_list(prompt_message: str): 59 | """ Solicita una entrada separada por comas y devuelve una lista de elementos. """ 60 | line = console.input(f"[bold green]{prompt_message}[/bold green] ").strip() 61 | return [x.strip() for x in line.split(",") if x.strip()] if line else [] 62 | 63 | def get_validated_port(prompt_message: str) -> str: 64 | """ Solicita y valida que el puerto ingresado sea numérico. """ 65 | while True: 66 | port = console.input(f"[bold green]{prompt_message}[/bold green] ").strip() 67 | if port == "": 68 | return "" 69 | if port.isdigit(): 70 | return port 71 | console.print("[red]Por favor, ingrese un número válido para el puerto.[/red]") 72 | 73 | def input_env_vars(): 74 | """ Solicita variables de entorno en formato KEY=VALUE. """ 75 | env_vars = {} 76 | console.print("[bold green]Ingrese las variables de entorno (formato KEY=VALUE). Deje en blanco para terminar.[/bold green]") 77 | while True: 78 | pair = console.input(" > ").strip() 79 | if not pair: 80 | break 81 | if "=" not in pair: 82 | console.print("[red]Formato inválido. Use KEY=VALUE.[/red]") 83 | continue 84 | key, value = pair.split("=", 1) 85 | env_vars[key.strip()] = value.strip() 86 | return env_vars 87 | 88 | def input_volumes(): 89 | """ Solicita mapeos de volúmenes en formato host:container. """ 90 | volumes = [] 91 | console.print("[bold green]Ingrese mapeos de volúmenes (formato /ruta/host:/ruta/container). Deje en blanco para terminar.[/bold green]") 92 | while True: 93 | mapping = console.input(" > ").strip() 94 | if not mapping: 95 | break 96 | if ":" not in mapping: 97 | console.print("[red]Formato inválido. Use /ruta/host:/ruta/container.[/red]") 98 | continue 99 | volumes.append(mapping) 100 | return volumes 101 | 102 | def input_service(): 103 | """ 104 | Recopila de manera interactiva la configuración de un servicio. 105 | Retorna el nombre del servicio y su configuración como diccionario. 106 | """ 107 | console.print(Panel("Configuración de un nuevo servicio", style="bold blue", expand=False)) 108 | name = get_non_empty_input("Ingrese el nombre del servicio:") 109 | service = {} 110 | 111 | # Seleccionar método de creación: imagen o build. 112 | method = "" 113 | while method not in ["1", "2"]: 114 | console.print("[bold yellow]Seleccione el método para el servicio:[/bold yellow]") 115 | console.print(" 1. Usar una imagen Docker") 116 | console.print(" 2. Especificar un build context") 117 | method = console.input("[bold green]Ingrese 1 o 2:[/bold green] ").strip() 118 | 119 | if method == "1": 120 | image = get_non_empty_input("Ingrese el nombre de la imagen Docker:") 121 | service["image"] = image 122 | else: 123 | context = get_non_empty_input("Ingrese el directorio de build (contexto):") 124 | build_dict = {"context": context} 125 | dockerfile = console.input("[bold green]Ingrese el nombre del Dockerfile (deje en blanco para 'Dockerfile'):[/bold green] ").strip() 126 | if dockerfile: 127 | build_dict["dockerfile"] = dockerfile 128 | service["build"] = build_dict 129 | 130 | # Opciones adicionales 131 | container_name = console.input("[bold green]Nombre del contenedor (opcional):[/bold green] ").strip() 132 | if container_name: 133 | service["container_name"] = container_name 134 | 135 | command = console.input("[bold green]Comando a ejecutar (opcional):[/bold green] ").strip() 136 | if command: 137 | service["command"] = command 138 | 139 | restart_policy = console.input("[bold green]Política de reinicio (ej. always, unless-stopped) (opcional):[/bold green] ").strip() 140 | if restart_policy: 141 | service["restart"] = restart_policy 142 | 143 | # Mapeo de puertos 144 | if Confirm.ask("[bold green]¿Desea mapear puertos?[/bold green]"): 145 | ports = [] 146 | while True: 147 | host_port = get_validated_port("Puerto en el host (deje en blanco para terminar):") 148 | if not host_port: 149 | break 150 | container_port = get_validated_port("Puerto en el contenedor:") 151 | if not container_port: 152 | console.print("[red]El puerto del contenedor es obligatorio si se ingresa el puerto del host.[/red]") 153 | continue 154 | ports.append(f"{host_port}:{container_port}") 155 | if ports: 156 | service["ports"] = ports 157 | 158 | # Variables de entorno 159 | if Confirm.ask("[bold green]¿Desea agregar variables de entorno?[/bold green]"): 160 | env_vars = input_env_vars() 161 | if env_vars: 162 | service["environment"] = env_vars 163 | 164 | # Mapeo de volúmenes 165 | if Confirm.ask("[bold green]¿Desea mapear volúmenes?[/bold green]"): 166 | vols = input_volumes() 167 | if vols: 168 | service["volumes"] = vols 169 | 170 | # Dependencias y redes 171 | depends_on = input_list("Ingrese servicios de los que depende (separados por coma) o deje en blanco:") 172 | if depends_on: 173 | service["depends_on"] = depends_on 174 | 175 | networks = input_list("Ingrese las redes a las que se conectará (separadas por coma) o deje en blanco:") 176 | if networks: 177 | service["networks"] = networks 178 | 179 | # Límites de recursos (opcional, para despliegues en Swarm) 180 | if Confirm.ask("[bold green]¿Desea especificar límites de recursos para el servicio?[/bold green]"): 181 | deploy = {} 182 | resources = {} 183 | limits = {} 184 | cpus = console.input("[bold green]Número máximo de CPUs (ej. 0.5) (deje en blanco para omitir):[/bold green] ").strip() 185 | if cpus: 186 | limits["cpus"] = cpus 187 | memory = console.input("[bold green]Memoria máxima (ej. 512M o 1G) (deje en blanco para omitir):[/bold green] ").strip() 188 | if memory: 189 | limits["memory"] = memory 190 | if limits: 191 | resources["limits"] = limits 192 | deploy["resources"] = resources 193 | if deploy: 194 | service["deploy"] = deploy 195 | 196 | return name, service 197 | 198 | def preview_configuration(compose_config: dict): 199 | """ Muestra un resumen de la configuración en forma de tabla. """ 200 | table = Table(title="Resumen de Docker Compose", show_lines=True) 201 | table.add_column("Sección", style="bold cyan") 202 | table.add_column("Detalles", style="white") 203 | 204 | table.add_row("Version", compose_config.get("version", "")) 205 | 206 | services = compose_config.get("services", {}) 207 | for svc_name, svc_conf in services.items(): 208 | details = "" 209 | for key, value in svc_conf.items(): 210 | details += f"[bold]{key}:[/bold] {value}\n" 211 | table.add_row(f"Servicio: {svc_name}", details.strip()) 212 | 213 | networks = compose_config.get("networks", {}) 214 | if networks: 215 | nets = ", ".join(networks.keys()) 216 | table.add_row("Redes", nets) 217 | 218 | console.print(table) 219 | 220 | def main(): 221 | print_banner() 222 | # Panel principal rosa ajustado al tamaño del texto (expand=False) 223 | console.print(Panel("Generador Interactivo de Docker Compose", style="bold magenta", expand=False)) 224 | compose_version = console.input("[bold green]Ingrese la versión de docker-compose (por defecto 3.8):[/bold green] ").strip() or "3.8" 225 | 226 | services = {} 227 | while True: 228 | name, service = input_service() 229 | services[name] = service 230 | if not Confirm.ask("[bold green]¿Desea agregar otro servicio?[/bold green]"): 231 | break 232 | 233 | compose_config = {"version": compose_version, "services": services} 234 | # Agregar redes a nivel superior si hay alguna definida en servicios 235 | all_networks = set() 236 | for svc in services.values(): 237 | nets = svc.get("networks", []) 238 | all_networks.update(nets) 239 | if all_networks: 240 | compose_config["networks"] = {net: {} for net in all_networks} 241 | 242 | # Imprimir título de vista previa sin panel azul 243 | console.print("[bold magenta]Vista previa de la configuración:[/bold magenta]") 244 | preview_configuration(compose_config) 245 | 246 | if not Confirm.ask("[bold green]¿Desea continuar y guardar el archivo?[/bold green]"): 247 | console.print("[red]Operación cancelada por el usuario.[/red]") 248 | sys.exit(0) 249 | 250 | output_filename = get_non_empty_input("Ingrese el nombre del archivo resultante (ej: docker-compose.yml):") 251 | output_path = os.path.join(os.getcwd(), output_filename) if not os.path.dirname(output_filename) else output_filename 252 | 253 | if os.path.exists(output_path): 254 | if not Confirm.ask(f"[yellow]El archivo '{output_path}' ya existe. ¿Desea sobrescribirlo?[/yellow]"): 255 | console.print("[red]Operación cancelada. No se sobreescribió el archivo existente.[/red]") 256 | sys.exit(0) 257 | 258 | try: 259 | yaml_output = yaml.dump(compose_config, sort_keys=False, default_flow_style=False) 260 | except Exception as e: 261 | console.print(f"[red]Error al generar YAML: {e}[/red]") 262 | sys.exit(1) 263 | 264 | try: 265 | with open(output_path, "w") as f: 266 | f.write(yaml_output) 267 | console.print(f"[bold blue]Archivo guardado exitosamente en:[/bold blue] {output_path}") 268 | except Exception as e: 269 | console.print(f"[bold red]Error al guardar el archivo:[/bold red] {e}") 270 | 271 | if __name__ == "__main__": 272 | main() 273 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # 🐳 DockerGen 3 | 4 | **DockerGen** es una herramienta interactiva de línea de comandos para generar archivos `docker-compose.yml` de manera guiada y personalizada. 5 | Utiliza una interfaz enriquecida con **rich** y se encarga de instalar automáticamente las dependencias necesarias (*rich* y *PyYAML*). 6 | 7 | ![Ejemplo DockerGen](img/foto.png) 8 | 9 | ## Características 10 | 11 | - **Generación Interactiva:** Configura múltiples servicios de Docker con opciones avanzadas (build context, mapeo de puertos, variables de entorno, volúmenes, redes, dependencias y límites de recursos). 12 | - **Vista Previa:** Muestra un resumen interactivo de la configuración antes de guardar. 13 | - **Manejo de Archivos:** Verifica si el archivo de salida ya existe y solicita confirmación para sobrescribirlo. 14 | - **Instalación Automática de Dependencias:** No es necesario instalar manualmente *rich* o *PyYAML*; DockerGen se encarga de ello. 15 | 16 | # ⚠️ Requisitos 17 | 18 | - **Python 3.7+** 19 | Verifica la versión ejecutando: 20 | ```bash 21 | python3 --version 22 | ``` 23 | 24 | ### Docker y Docker Compose 25 | Para utilizar el archivo generado, asegúrate de tener Docker y Docker Compose instalados. 26 | 27 | # 🛠️ Instalación 28 | Clona el repositorio o descarga el código fuente: 29 | 30 | ```bash 31 | git clone https://github.com/Mayky23/DockerGen.git 32 | cd DockerGen 33 | ``` 34 | 35 | (Opcional pero recomendado) Crea un entorno virtual: 36 | ```bash 37 | python3 -m venv venv 38 | source venv/bin/activate # En Windows: venv\Scripts\activate 39 | ``` 40 | 41 | # 🖥️​ Uso 42 | 43 | ## Instalación de dependencias 44 | 45 | Antes de ejecutar el script, asegúrate de instalar las dependencias necesarias con: 46 | ```bash 47 | pip install -r requirements.txt 48 | ``` 49 | Para iniciar DockerGen, ejecuta: 50 | ```bash 51 | python3 DockerGen.py 52 | ``` 53 | # Uso del Archivo Generado 54 | Una vez generado el archivo docker-compose.yml, puedes usarlo con Docker Compose: 55 | 56 | ## Para iniciar los servicios: 57 | ```bash 58 | docker-compose -f docker-compose.yml up -d 59 | ``` 60 | ## Para detener y eliminar los contenedores: 61 | ```bash 62 | docker-compose -f docker-compose.yml down 63 | ``` 64 | ## Creador : 65 | - [@Mayky23](https://github.com/Mayky23) 66 | 67 | ## Contribuyente : 68 | - [@Tenvid](https://github.com/Tenvid) 69 | -------------------------------------------------------------------------------- /img/foto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mayky23/DockerGen/3bf029c9bfb93d9edb7e04e0b73aa3c3c2e90ac2/img/foto.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | rich 2 | PyYAML 3 | --------------------------------------------------------------------------------