pkeffect

Untitled

Apr 8th, 2025
649
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 11.76 KB | None | 0 0
  1. #!/usr/bin/env python3
  2. """
  3. Docker Compose Startup Helper (Python Version)
  4.  
  5. This script automatically finds and runs Docker Compose files with
  6. proper error handling and status reporting.
  7.  
  8. Features:
  9. - Automatically detects common Docker Compose filenames
  10. - Provides clear visual feedback with colored output
  11. - Handles errors gracefully with descriptive messages
  12. - Cross-platform support (Windows, macOS, Linux)
  13. - Displays running containers after successful startup
  14.  
  15. Usage:
  16.    python docker_compose_helper.py
  17.  
  18. Returns:
  19.    0: Success - Docker Compose started successfully
  20.    1: Error - No valid Docker Compose file found
  21.    2: Error - Docker Compose command failed
  22.    3: Error - Docker daemon not running
  23.    4: Error - Docker Compose not available
  24.    130: Error - Script interrupted by user
  25. """
  26.  
  27. import os
  28. import sys
  29. import subprocess
  30. from pathlib import Path
  31. from typing import List, Optional, Tuple
  32.  
  33. # ANSI color codes for terminal output
  34. class Colors:
  35.     """ANSI color codes for terminal output formatting."""
  36.     GREEN = "\033[0;32m"   # Success messages
  37.     RED = "\033[0;31m"     # Error messages
  38.     YELLOW = "\033[0;33m"  # Warning or information messages
  39.     BLUE = "\033[0;34m"    # Status or processing messages
  40.     CYAN = "\033[0;36m"    # Additional highlighting
  41.     BOLD = "\033[1m"       # Bold text
  42.     NC = "\033[0m"         # No Color (reset)
  43.  
  44. # Status icons for visual feedback
  45. class Icons:
  46.     """Unicode icons for visual status indication."""
  47.     SUCCESS = "✅"  # Success indicator
  48.     ERROR = "❌"    # Error indicator
  49.     INFO = "â„šī¸"     # Information indicator
  50.     ROCKET = "🚀"  # Process starting indicator
  51.     FOLDER = "📂"  # Directory/location indicator
  52.     SEARCH = "🔍"  # Search/check indicator
  53.     TIP = "💡"     # Suggestion/tip indicator
  54.     DOCKER = "đŸŗ"  # Docker-related indicator
  55.     LIST = "📋"    # List/display indicator
  56.     WARNING = "âš ī¸"  # Warning indicator
  57.     CHECK = "✓"    # Check/verification indicator
  58.  
  59. def print_status(icon: str, message: str, color: str) -> None:
  60.     """
  61.    Print a colored status message with an icon.
  62.    
  63.    Args:
  64.        icon (str): The icon to display before the message.
  65.        message (str): The message to display.
  66.        color (str): The ANSI color code to use.
  67.    """
  68.     print(f"{color}{icon} {message}{Colors.NC}")
  69.  
  70. def print_header(title: str) -> None:
  71.     """
  72.    Print a formatted header with title.
  73.    
  74.    Args:
  75.        title (str): The title text for the header.
  76.    """
  77.     border = "=" * (len(title) + 10)
  78.     print(f"\n{Colors.BOLD}{Colors.BLUE}{border}")
  79.     print(f"    {title}")
  80.     print(f"{border}{Colors.NC}\n")
  81.  
  82. def check_command_exists(command: str) -> bool:
  83.     """
  84.    Check if a command exists and is executable in the system path.
  85.    
  86.    Args:
  87.        command (str): The command to check.
  88.        
  89.    Returns:
  90.        bool: True if the command exists and is executable, False otherwise.
  91.    """
  92.     try:
  93.         # Use 'which' on Unix-like systems and 'where' on Windows
  94.         check_cmd = "which" if sys.platform != "win32" else "where"
  95.         result = subprocess.run(
  96.             [check_cmd, command],
  97.             stdout=subprocess.PIPE,
  98.             stderr=subprocess.PIPE,
  99.             text=True,
  100.             check=False
  101.         )
  102.         return result.returncode == 0
  103.     except Exception:
  104.         return False
  105.  
  106. def run_command(command: List[str]) -> Tuple[int, str, str]:
  107.     """
  108.    Run a system command and return its results.
  109.    
  110.    Args:
  111.        command (List[str]): The command to run as a list of strings.
  112.        
  113.    Returns:
  114.        Tuple[int, str, str]: A tuple containing:
  115.            - Exit code (int)
  116.            - Standard output (str)
  117.            - Standard error (str)
  118.    """
  119.     try:
  120.         process = subprocess.Popen(
  121.             command,
  122.             stdout=subprocess.PIPE,
  123.             stderr=subprocess.PIPE,
  124.             text=True,
  125.             universal_newlines=True
  126.         )
  127.         stdout, stderr = process.communicate()
  128.         return process.returncode, stdout, stderr
  129.     except Exception as e:
  130.         return 1, "", str(e)
  131.  
  132. def check_docker() -> bool:
  133.     """
  134.    Check if Docker daemon is running.
  135.    
  136.    Returns:
  137.        bool: True if Docker daemon is running, False otherwise.
  138.    """
  139.     print_status(Icons.SEARCH, "Checking if Docker daemon is running...", Colors.YELLOW)
  140.    
  141.     # First check if docker command is available
  142.     if not check_command_exists("docker"):
  143.         print_status(Icons.ERROR, "Docker command not found! Please install Docker first.", Colors.RED)
  144.         return False
  145.        
  146.     # Then check if docker daemon is running
  147.     exit_code, _, stderr = run_command(["docker", "info"])
  148.     if exit_code != 0:
  149.         print_status(Icons.ERROR, "Docker daemon is not running! Please start Docker first.", Colors.RED)
  150.         if stderr:
  151.             print(f"{Colors.RED}Error details: {stderr.strip()}{Colors.NC}")
  152.         return False
  153.        
  154.     print_status(Icons.SUCCESS, "Docker daemon is running.", Colors.GREEN)
  155.     return True
  156.  
  157. def check_docker_compose() -> bool:
  158.     """
  159.    Check if Docker Compose is available.
  160.    
  161.    Returns:
  162.        bool: True if Docker Compose is available, False otherwise.
  163.    """
  164.     print_status(Icons.SEARCH, "Checking if Docker Compose is available...", Colors.YELLOW)
  165.    
  166.     exit_code, stdout, _ = run_command(["docker", "compose", "version"])
  167.     if exit_code != 0:
  168.         print_status(Icons.ERROR, "Docker Compose is not available. Please install it first.", Colors.RED)
  169.         return False
  170.        
  171.     # Extract and display version for informational purposes
  172.     if stdout:
  173.         version_line = stdout.strip().split('\n')[0]
  174.         print_status(Icons.SUCCESS, f"Docker Compose is available: {version_line}", Colors.GREEN)
  175.     else:
  176.         print_status(Icons.SUCCESS, "Docker Compose is available.", Colors.GREEN)
  177.        
  178.     return True
  179.  
  180. def find_compose_file(current_dir: Path) -> Optional[str]:
  181.     """
  182.    Find a Docker Compose file in the current directory.
  183.    
  184.    Args:
  185.        current_dir (Path): The directory to search in.
  186.        
  187.    Returns:
  188.        Optional[str]: The name of the found compose file, or None if not found.
  189.    """
  190.     # Standard Docker Compose filenames in order of preference
  191.     compose_files = [
  192.         "compose.yaml",
  193.         "compose.yml",
  194.         "docker-compose.yaml",
  195.         "docker-compose.yml"
  196.     ]
  197.    
  198.     print_status(Icons.SEARCH, "Searching for Docker Compose files:", Colors.YELLOW)
  199.    
  200.     for file in compose_files:
  201.         file_path = current_dir / file
  202.         if file_path.exists():
  203.             print_status(Icons.SUCCESS, f"Found: {file}", Colors.GREEN)
  204.             return file
  205.         else:
  206.             print_status(Icons.INFO, f"Not found: {file}", Colors.YELLOW)
  207.    
  208.     return None
  209.  
  210. def display_running_containers(compose_file: str) -> None:
  211.     """
  212.    Display currently running containers from the Docker Compose setup.
  213.    
  214.    Args:
  215.        compose_file (str): The Docker Compose file name.
  216.    """
  217.     print()
  218.     print_status(Icons.LIST, "Currently running containers:", Colors.BLUE)
  219.    
  220.     # Get running containers
  221.     exit_code, stdout, stderr = run_command(["docker", "compose", "-f", compose_file, "ps"])
  222.    
  223.     if stdout:
  224.         print(f"{Colors.CYAN}{stdout}{Colors.NC}")
  225.     elif exit_code != 0:
  226.         print_status(Icons.WARNING, "Could not list containers.", Colors.YELLOW)
  227.         if stderr:
  228.             print(f"{Colors.RED}{stderr}{Colors.NC}")
  229.  
  230. def main() -> int:
  231.     """
  232.    Main function to run the Docker Compose startup script.
  233.    
  234.    This function:
  235.    1. Checks prerequisites (Docker daemon, Docker Compose)
  236.    2. Finds a Docker Compose file in the current directory
  237.    3. Starts the Docker Compose services
  238.    4. Displays running containers on success
  239.    
  240.    Returns:
  241.        int: Exit code indicating success (0) or specific failure (1-4, 130)
  242.    """
  243.     try:
  244.         # Display script header
  245.         print_header("Docker Compose Startup Helper")
  246.        
  247.         # Check prerequisites
  248.         print_status(Icons.INFO, "Checking prerequisites...", Colors.BLUE)
  249.        
  250.         if not check_docker():
  251.             return 3
  252.        
  253.         if not check_docker_compose():
  254.             return 4
  255.        
  256.         # Get current directory
  257.         current_dir = Path.cwd()
  258.         print()
  259.         print_status(Icons.FOLDER, f"Working directory: {current_dir}", Colors.BLUE)
  260.        
  261.         # Find Docker Compose file
  262.         compose_file = find_compose_file(current_dir)
  263.        
  264.         if not compose_file:
  265.             print()
  266.             print_status(Icons.ERROR, f"Error: No valid Docker Compose file found in {current_dir}!", Colors.RED)
  267.             print()
  268.             print_status(Icons.TIP, "Tip: Create one of these files:", Colors.YELLOW)
  269.             print(f"{Colors.YELLOW}  - compose.yaml (recommended)")
  270.             print(f"  - compose.yml")
  271.             print(f"  - docker-compose.yaml")
  272.             print(f"  - docker-compose.yml{Colors.NC}")
  273.             return 1
  274.        
  275.         # Run Docker Compose
  276.         print()
  277.         print_status(Icons.ROCKET, f"Starting services from {compose_file}...", Colors.BLUE)
  278.         print()
  279.        
  280.         exit_code, stdout, stderr = run_command(["docker", "compose", "-f", compose_file, "up", "-d"])
  281.        
  282.         if stdout:
  283.             print(f"{Colors.CYAN}{stdout}{Colors.NC}")
  284.        
  285.         if exit_code == 0:
  286.             print()
  287.             print_status(Icons.SUCCESS, "Services started successfully!", Colors.GREEN)
  288.            
  289.             # Display running containers
  290.             display_running_containers(compose_file)
  291.         else:
  292.             print()
  293.             print_status(Icons.ERROR, f"Docker Compose failed with exit code: {exit_code}", Colors.RED)
  294.            
  295.             if stderr:
  296.                 print(f"{Colors.RED}{stderr}{Colors.NC}")
  297.                
  298.             print_status(Icons.SEARCH, "Possible issues:", Colors.YELLOW)
  299.             print(f"{Colors.YELLOW}  - Check the syntax in {compose_file}")
  300.             print("  - Ensure Docker daemon is running properly")
  301.             print("  - Verify network connectivity for image pulling")
  302.             print("  - Check for port conflicts with existing services")
  303.             print(f"  - Verify environment variables referenced in {compose_file}{Colors.NC}")
  304.             return 2
  305.        
  306.         # Success message with instructions
  307.         print()
  308.         print_status(Icons.TIP, "Useful commands:", Colors.CYAN)
  309.         print(f"{Colors.CYAN}  - View logs: docker compose -f {compose_file} logs")
  310.         print(f"  - Stop services: docker compose -f {compose_file} down")
  311.         print(f"  - Restart services: docker compose -f {compose_file} restart{Colors.NC}")
  312.        
  313.         # Optional pause to view results
  314.         print()
  315.         input(f"{Colors.BOLD}Press Enter to exit...{Colors.NC}")
  316.         return 0
  317.        
  318.     except KeyboardInterrupt:
  319.         print("\n")
  320.         print_status(Icons.INFO, "Script interrupted by user.", Colors.YELLOW)
  321.         return 130
  322.  
  323. if __name__ == "__main__":
  324.     try:
  325.         sys.exit(main())
  326.     except KeyboardInterrupt:
  327.         print("\n")
  328.         print_status(Icons.INFO, "Script interrupted by user.", Colors.YELLOW)
  329.         sys.exit(130)
  330.     except Exception as e:
  331.         print("\n")
  332.         print_status(Icons.ERROR, f"Unexpected error occurred:", Colors.RED)
  333.         print(f"{Colors.RED}{str(e)}{Colors.NC}")
  334.        
  335.         # Print stack trace in development environments
  336.         if os.environ.get("DEBUG", "").lower() in ("1", "true", "yes"):
  337.             import traceback
  338.             print(f"{Colors.RED}{traceback.format_exc()}{Colors.NC}")
  339.            
  340.         sys.exit(1)
Advertisement
Add Comment
Please, Sign In to add comment