"""Supabase configuration and deployment screen.""" import os import subprocess import threading import tkinter as tk from installer import screen as ui from installer.core.env import PROJECT_ROOT, _normalize_cluster_env, _parse_bool class SupabaseScreenMixin: """Supabase configuration and deployment screen.""" def _render_supabase_config_page(self): # Letterhead at top right (matching welcome screen theme) content_width = self.bg_canvas.winfo_width() or 975 right_margin = content_width - 48 ui.canvas_text(self, right_margin, 40, 'Prole', fill='#6e6e73', font=('SF Pro Text', 32, 'bold'), anchor='ne') ui.canvas_text(self, right_margin, 85, "Infrastructure Automated.", fill='#6e6e73', font=('SF Pro Text', 18), anchor='ne') self._render_title('Prole::Supabase', y=150) description = ( "Launches the Supabase open-source stack via supabase/deploy.sh.\n" "Default mode is local (Docker Compose). Set SUPABASE_DEPLOY_MODE=k3d\n" "to deploy into the 'supabase' Kubernetes namespace." ) self._render_paragraph(description, y=210) y = 280 self._supabase_status_var = tk.StringVar(value="Ready") status_item = ui.canvas_text(self, 48, y, "Status: Ready", fill='black', font=('SF Pro Text', 12)) def update_status_text(*args): try: self.bg_canvas.itemconfig(status_item, text=f"Status: {self._supabase_status_var.get()}") except Exception: pass self._supabase_status_var.trace_add('write', update_status_text) # Standardized Console Output self._supabase_console = self._create_console_output(y=320, title="Deployment Output", width=880, height=380) y = 740 # Deploy Supabase Button self._supabase_deploy_button = tk.Button( self.bg_canvas, text="Deploy Supabase", command=self.run_supabase_deploy, bg='#F5F5DC', fg='black', activebackground='#E5E5D5', activeforeground='black', highlightbackground='#F5F5DC', highlightcolor='#F5F5DC', highlightthickness=0, relief='flat', bd=0, cursor='hand2', disabledforeground='#8B8B7A', font=('SF Pro Text', 11), padx=20, pady=10, ) btn_window = self.bg_canvas.create_window(48, y, window=self._supabase_deploy_button, anchor='nw') self._overlay_widgets.append(self._supabase_deploy_button) self._canvas_items.append(btn_window) def run_supabase_deploy(self): if getattr(self, '_supabase_deploying', False): return self._supabase_deploying = True self._supabase_deploy_button.configure(state='disabled') self._supabase_status_var.set("Deploying...") self._supabase_success = False self._supabase_console.clear() self.update_footer() def worker(): namespace = (self.db_namespace.get() or '').strip() or os.environ.get('NAMESPACE') or 'default' env = os.environ.copy() env["PROLE_HOME"] = str(PROJECT_ROOT) env["PROLE_SERVICE"] = str(PROJECT_ROOT) env["NAMESPACE"] = namespace self._supabase_console.write("Starting Supabase deployment...\n") script_path = PROJECT_ROOT / "supabase" / "deploy.sh" if not script_path.exists(): self._supabase_console.write(f"Error: {script_path} not found.\n") self.safe_after(lambda: self._supabase_status_var.set("Failed (Script not found)")) self.safe_after(lambda: self._supabase_deploy_button.configure(state='normal')) self._supabase_deploying = False return mode = (os.environ.get("SUPABASE_DEPLOY_MODE") or os.environ.get("SUPABASE_MODE") or "").strip() if not mode: # Match cluster environment to supabase deploy mode cluster_env = _normalize_cluster_env(self.cluster_env.get()) if cluster_env == 'dev': mode = "k3d" elif cluster_env in ('service', 'prod'): mode = "k8s" else: mode = "k3d" args = ["--mode", mode] cfg_path = None try: if self._cfg_path_override and self._cfg_path_override.exists(): cfg_path = self._cfg_path_override else: conf_dir = self._resolve_prole_conf_dir() candidate = conf_dir / "prole.cfg" if candidate.exists(): cfg_path = candidate if not cfg_path: candidate = PROJECT_ROOT / "conf" / "prole.cfg" if candidate.exists(): cfg_path = candidate except Exception: cfg_path = None if cfg_path: args.extend(["-c", str(cfg_path)]) if _parse_bool(os.environ.get("SUPABASE_USE_DEV_COMPOSE"), False): args.append("--with-dev-helpers") if _parse_bool(os.environ.get("SUPABASE_FOREGROUND"), False): args.append("--foreground") self._supabase_console.write(f"Mode: {mode}\n") cmd = ["bash", str(script_path)] + args try: proc = subprocess.Popen( cmd, cwd=str(PROJECT_ROOT), env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1, ) except Exception as e: self._supabase_console.write(f"Failed to start deploy: {e}\n") self.safe_after(lambda: self._supabase_status_var.set("Failed (Launch error)")) self.safe_after(lambda: self._supabase_deploy_button.configure(state='normal')) self._supabase_deploying = False return if proc.stdout: for line in iter(proc.stdout.readline, ''): if line: self._supabase_console.write(line) proc.stdout.close() rc = proc.wait() if rc == 0: self._supabase_console.write("\nSupabase deployment completed successfully.\n") ports_script = PROJECT_ROOT / "etc" / "init_supabase_ports.sh" self.safe_after(lambda: self._supabase_status_var.set("Configuring Ports...")) if not ports_script.exists(): self._supabase_success = False self.safe_after(lambda: self._supabase_status_var.set("Failed (Ports script not found)")) self._supabase_console.write(f"Error: {ports_script} not found.\n") else: self._supabase_console.write(f"\nConfiguring Supabase ports for namespace '{namespace}'...\n") self._supabase_console.write(f"> bash etc/init_supabase_ports.sh -n {namespace}\n\n") try: ports_proc = subprocess.Popen( ["bash", str(ports_script), "-n", namespace], cwd=str(PROJECT_ROOT), env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1, ) except Exception as e: self._supabase_success = False self.safe_after(lambda: self._supabase_status_var.set("Failed (Ports launch error)")) self._supabase_console.write(f"Failed to start port wiring: {e}\n") else: if ports_proc.stdout: for line in iter(ports_proc.stdout.readline, ''): if line: self._supabase_console.write(line) ports_proc.stdout.close() ports_rc = ports_proc.wait() if ports_rc == 0: self._supabase_success = True self.safe_after(lambda: self._supabase_status_var.set("Deployed Successfully")) self._supabase_console.write("\nSupabase port wiring completed successfully.\n") else: self._supabase_success = False self.safe_after(lambda: self._supabase_status_var.set(f"Failed (Ports code {ports_rc})")) self._supabase_console.write(f"\nSupabase port wiring failed with exit code {ports_rc}.\n") else: self._supabase_success = False self.safe_after(lambda: self._supabase_status_var.set(f"Failed (Code {rc})")) self._supabase_console.write(f"\nSupabase deployment failed with exit code {rc}.\n") self._supabase_deploying = False self.safe_after(lambda: self._supabase_deploy_button.configure(state='normal')) self.safe_after(self.update_footer) threading.Thread(target=worker, daemon=True).start()