mirror of
https://github.com/dredx/prole.git
synced 2026-09-27 16:24:30 +00:00
Kong API Gateway (replacing prole nginx): - Add etc/init_kong.sh provisioning script (DB-less mode, prole-db namespace) - Add kong-deployment.yaml and kong-service.yaml manifests - Rewire ingress rules (svc/git/api.prole.org) to prole-db-kong:8000 - Update kustomization.yaml to reference kong manifests PostgREST & DB Manager in prole-db namespace: - Add etc/init_postgrest.sh and etc/init_db_manager.sh scripts - Add postgrest/db-manager deployment and service manifests - Add src/db-manager/ Node.js REST endpoint for backup triggers - Default NAMESPACE changed to prole-db in both scripts Docker image pre-load from PROLE_DATA/docker-import: - Add _preload_docker_images() to init_common_services.sh - Scan for .tar files exported by final_deployment.sh - Import via k3d image import (k3d) or ctr (k3s) before deployments - Increase rollout timeouts to 300s (configurable via ROLLOUT_TIMEOUT) in init_openbao.sh, init_opentofu.sh, init_garage_store.sh, init_registry.sh OpenTofu password resolution fix: - Add Kubernetes secret fallback in resolve_admin_password() - Change hard exit 1 to graceful return 1 with warning - Wrap call in if-guard so set -e doesn't abort the script chain Milestone fix (init scripts not running): - Add init_kong.sh, init_postgrest.sh, init_db_manager.sh to InitializationScriptsMilestone.execute() script list and arg branches - Previously only actions.py had these; milestones.py was missing them Installer integration: - Add Kong/PostgREST/DB Manager to silent installer _step_init_scripts - Add corresponding tabs and execution blocks in UI services.py
213 lines
9.4 KiB
Python
213 lines
9.4 KiB
Python
"""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('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()
|