#!/usr/bin/env python3 import os import subprocess import sys # Support dashes in command names COMMAND_TO_METHOD = {} METHOD_TO_COMMAND = {method: command for command, method in COMMAND_TO_METHOD.items()} WAIT_FOR_PROCESS = "wait_for_process" class ProdHelper: def __init__(self): self.last_exit_status = 0 # Core logic def call(self, *args, **kwargs): if not args: return self.compose(*args, **kwargs) command, rest = args[0], args[1:] method_name = COMMAND_TO_METHOD.get(command, command) method = getattr(self, method_name, None) if callable(method) and not method_name.startswith("_") and method_name != "call": return method(*rest, **kwargs) return self.compose(*args, **kwargs) def compose(self, *args, **kwargs): command = self._compose_command(*args, **kwargs) if not kwargs.get("silent"): print(f"Running: {command}") if kwargs.get("execution_mode") == WAIT_FOR_PROCESS: self._wait_for_process_with_logging(command) else: os.execv("/bin/sh", ["/bin/sh", "-c", command]) # Primary command wrappers def up(self, *args, **kwargs): self.compose("up", "--remove-orphans", *args, **kwargs) def down(self, *args, **kwargs): self.compose("down", "--remove-orphans", *args, **kwargs) def logs(self, *args, **kwargs): self.compose("logs", "-f", *args, **kwargs) def ps(self, *args, **kwargs): self.compose("ps", *args, **kwargs) def bash_completions(self, *args, **kwargs): completions = sorted( METHOD_TO_COMMAND.get(name, name) for name in vars(ProdHelper) if not name.startswith("_") and name != "call" and callable(getattr(ProdHelper, name)) ) print(" ".join(completions)) # Private helpers def _wait_for_process_with_logging(self, command): process = subprocess.Popen( ["/bin/sh", "-c", f"{command} 2>&1"], stdout=subprocess.PIPE, text=True, ) for line in process.stdout: print(line, end="") process.wait() self.last_exit_status = process.returncode def _compose_command(self, *args, **kwargs): environment = kwargs.get("environment", "production") return f"cd {self._project_root()} && docker compose -f docker-compose.{environment}.yml {' '.join(args)}" def _project_root(self): return os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) if __name__ == "__main__": ProdHelper().call(*sys.argv[1:])