feat: video-flow initial commit
- app.py: Streamlit UI for video generation workflow - main_flow.py: CLI tool with argparse support - modules/: Business logic modules (script_gen, image_gen, video_gen, composer, etc.) - config.py: Configuration with API keys and paths - requirements.txt: Python dependencies - docs/: System prompt documentation
This commit is contained in:
255
deploy.py
Normal file
255
deploy.py
Normal file
@@ -0,0 +1,255 @@
|
||||
"""
|
||||
Gloda Video Factory - Deployment Script
|
||||
One-click deployment to remote server using Fabric.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from fabric import Connection, Config
|
||||
from invoke import task
|
||||
|
||||
# Load environment variables
|
||||
load_dotenv()
|
||||
|
||||
# Server configuration
|
||||
SERVER_IP = os.getenv("SERVER_IP", "")
|
||||
SERVER_USER = os.getenv("SERVER_USER", "root")
|
||||
SERVER_PASS = os.getenv("SERVER_PASS", "")
|
||||
|
||||
# Remote paths
|
||||
REMOTE_APP_DIR = "/opt/gloda-factory"
|
||||
REMOTE_VENV = f"{REMOTE_APP_DIR}/venv"
|
||||
|
||||
# Files to upload
|
||||
LOCAL_FILES = [
|
||||
"config.py",
|
||||
"web_app.py",
|
||||
"requirements.txt",
|
||||
".env",
|
||||
"modules/__init__.py",
|
||||
"modules/utils.py",
|
||||
"modules/brain.py",
|
||||
"modules/factory.py",
|
||||
"modules/editor.py",
|
||||
]
|
||||
|
||||
|
||||
def get_connection() -> Connection:
|
||||
"""Create SSH connection to remote server."""
|
||||
if not SERVER_IP or not SERVER_PASS:
|
||||
raise ValueError("SERVER_IP and SERVER_PASS must be set in .env")
|
||||
|
||||
config = Config(overrides={"sudo": {"password": SERVER_PASS}})
|
||||
return Connection(
|
||||
host=SERVER_IP,
|
||||
user=SERVER_USER,
|
||||
connect_kwargs={"password": SERVER_PASS},
|
||||
config=config
|
||||
)
|
||||
|
||||
|
||||
def deploy():
|
||||
"""Full deployment: setup server, upload code, start app."""
|
||||
print("🚀 Starting deployment...")
|
||||
|
||||
conn = get_connection()
|
||||
|
||||
# Step 1: Install system dependencies
|
||||
print("\n📦 Step 1/5: Installing system dependencies...")
|
||||
install_dependencies(conn)
|
||||
|
||||
# Step 2: Create app directory
|
||||
print("\n📁 Step 2/5: Setting up directories...")
|
||||
setup_directories(conn)
|
||||
|
||||
# Step 3: Upload code
|
||||
print("\n📤 Step 3/5: Uploading code...")
|
||||
upload_code(conn)
|
||||
|
||||
# Step 4: Setup Python environment
|
||||
print("\n🐍 Step 4/5: Setting up Python environment...")
|
||||
setup_python(conn)
|
||||
|
||||
# Step 5: Start application
|
||||
print("\n🎬 Step 5/5: Starting application...")
|
||||
start_app(conn)
|
||||
|
||||
print(f"\n✅ Deployment complete!")
|
||||
print(f"🌐 Access the app at: http://{SERVER_IP}:8501")
|
||||
|
||||
conn.close()
|
||||
|
||||
|
||||
def install_dependencies(conn: Connection):
|
||||
"""Install system-level dependencies."""
|
||||
commands = [
|
||||
"apt-get update -qq",
|
||||
"apt-get install -y -qq python3 python3-pip python3-venv",
|
||||
"apt-get install -y -qq ffmpeg imagemagick",
|
||||
"apt-get install -y -qq fonts-liberation fonts-dejavu-core",
|
||||
]
|
||||
|
||||
for cmd in commands:
|
||||
print(f" Running: {cmd[:50]}...")
|
||||
conn.sudo(cmd, hide=True)
|
||||
|
||||
# Configure ImageMagick policy (allow PDF/SVG for text rendering)
|
||||
policy_fix = """
|
||||
sed -i 's/<policy domain="path" rights="none" pattern="@\\*"/<policy domain="path" rights="read|write" pattern="@*"/g' /etc/ImageMagick-6/policy.xml 2>/dev/null || true
|
||||
"""
|
||||
conn.sudo(policy_fix, hide=True, warn=True)
|
||||
|
||||
print(" ✅ System dependencies installed")
|
||||
|
||||
|
||||
def setup_directories(conn: Connection):
|
||||
"""Create application directories on remote server."""
|
||||
conn.sudo(f"mkdir -p {REMOTE_APP_DIR}/modules", hide=True)
|
||||
conn.sudo(f"mkdir -p {REMOTE_APP_DIR}/output", hide=True)
|
||||
conn.sudo(f"mkdir -p {REMOTE_APP_DIR}/assets/fonts", hide=True)
|
||||
conn.sudo(f"chown -R {SERVER_USER}:{SERVER_USER} {REMOTE_APP_DIR}", hide=True)
|
||||
|
||||
print(f" ✅ Directories created at {REMOTE_APP_DIR}")
|
||||
|
||||
|
||||
def upload_code(conn: Connection):
|
||||
"""Upload application code to remote server."""
|
||||
local_base = Path(__file__).parent
|
||||
|
||||
for file_path in LOCAL_FILES:
|
||||
local_file = local_base / file_path
|
||||
remote_file = f"{REMOTE_APP_DIR}/{file_path}"
|
||||
|
||||
if local_file.exists():
|
||||
# Ensure remote directory exists
|
||||
remote_dir = str(Path(remote_file).parent)
|
||||
conn.run(f"mkdir -p {remote_dir}", hide=True)
|
||||
|
||||
# Upload file
|
||||
conn.put(str(local_file), remote_file)
|
||||
print(f" ✅ Uploaded: {file_path}")
|
||||
else:
|
||||
print(f" ⚠️ Skipped (not found): {file_path}")
|
||||
|
||||
print(" ✅ Code uploaded")
|
||||
|
||||
|
||||
def setup_python(conn: Connection):
|
||||
"""Setup Python virtual environment and install dependencies."""
|
||||
with conn.cd(REMOTE_APP_DIR):
|
||||
# Create virtual environment
|
||||
conn.run(f"python3 -m venv {REMOTE_VENV}", hide=True)
|
||||
|
||||
# Upgrade pip
|
||||
conn.run(f"{REMOTE_VENV}/bin/pip install --upgrade pip -q", hide=True)
|
||||
|
||||
# Install requirements
|
||||
conn.run(f"{REMOTE_VENV}/bin/pip install -r requirements.txt -q", hide=True)
|
||||
|
||||
print(" ✅ Python environment ready")
|
||||
|
||||
|
||||
def start_app(conn: Connection):
|
||||
"""Start the Streamlit application."""
|
||||
# Stop existing process if any
|
||||
conn.run("pkill -f 'streamlit run web_app.py' || true", hide=True, warn=True)
|
||||
|
||||
# Start in background with nohup
|
||||
start_cmd = f"""
|
||||
cd {REMOTE_APP_DIR} && \
|
||||
nohup {REMOTE_VENV}/bin/streamlit run web_app.py \
|
||||
--server.port 8501 \
|
||||
--server.address 0.0.0.0 \
|
||||
--server.headless true \
|
||||
--browser.gatherUsageStats false \
|
||||
> /var/log/gloda-factory.log 2>&1 &
|
||||
"""
|
||||
conn.run(start_cmd, hide=True)
|
||||
|
||||
# Wait and verify
|
||||
import time
|
||||
time.sleep(3)
|
||||
|
||||
result = conn.run("pgrep -f 'streamlit run web_app.py'", hide=True, warn=True)
|
||||
if result.ok:
|
||||
print(f" ✅ Application started (PID: {result.stdout.strip()})")
|
||||
else:
|
||||
print(" ⚠️ Application may not have started. Check logs.")
|
||||
|
||||
|
||||
def stop_app():
|
||||
"""Stop the running application."""
|
||||
print("🛑 Stopping application...")
|
||||
conn = get_connection()
|
||||
conn.run("pkill -f 'streamlit run web_app.py' || true", hide=True, warn=True)
|
||||
conn.close()
|
||||
print("✅ Application stopped")
|
||||
|
||||
|
||||
def logs():
|
||||
"""Show application logs."""
|
||||
print("📋 Recent logs:")
|
||||
conn = get_connection()
|
||||
conn.run("tail -50 /var/log/gloda-factory.log", warn=True)
|
||||
conn.close()
|
||||
|
||||
|
||||
def status():
|
||||
"""Check application status."""
|
||||
print("📊 Checking status...")
|
||||
conn = get_connection()
|
||||
|
||||
result = conn.run("pgrep -f 'streamlit run web_app.py'", hide=True, warn=True)
|
||||
if result.ok:
|
||||
print(f"✅ Application is running (PID: {result.stdout.strip()})")
|
||||
print(f"🌐 URL: http://{SERVER_IP}:8501")
|
||||
else:
|
||||
print("❌ Application is not running")
|
||||
|
||||
conn.close()
|
||||
|
||||
|
||||
def restart():
|
||||
"""Restart the application."""
|
||||
print("🔄 Restarting application...")
|
||||
conn = get_connection()
|
||||
|
||||
# Stop
|
||||
conn.run("pkill -f 'streamlit run web_app.py' || true", hide=True, warn=True)
|
||||
|
||||
import time
|
||||
time.sleep(2)
|
||||
|
||||
# Start
|
||||
start_app(conn)
|
||||
conn.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="Gloda Factory Deployment Tool")
|
||||
parser.add_argument(
|
||||
"command",
|
||||
choices=["deploy", "start", "stop", "restart", "status", "logs"],
|
||||
help="Deployment command to run"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
commands = {
|
||||
"deploy": deploy,
|
||||
"stop": stop_app,
|
||||
"status": status,
|
||||
"logs": logs,
|
||||
"restart": restart,
|
||||
"start": lambda: start_app(get_connection()),
|
||||
}
|
||||
|
||||
commands[args.command]()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user