2025-05-29 23:50:41 +08:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
"""
|
|
|
|
|
Development environment management script for the Crypto Trading Bot Dashboard.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import subprocess
|
|
|
|
|
import sys
|
|
|
|
|
import time
|
|
|
|
|
import os
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def run_command(command: str, check: bool = True) -> subprocess.CompletedProcess:
|
|
|
|
|
"""Run a shell command and return the result."""
|
|
|
|
|
print(f"Running: {command}")
|
|
|
|
|
return subprocess.run(command, shell=True, check=check)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def check_docker():
|
|
|
|
|
"""Check if Docker is installed and running."""
|
|
|
|
|
try:
|
|
|
|
|
result = subprocess.run(
|
|
|
|
|
"docker --version", shell=True, capture_output=True, text=True
|
|
|
|
|
)
|
|
|
|
|
if result.returncode != 0:
|
|
|
|
|
print("❌ Docker is not installed or not in PATH")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
result = subprocess.run(
|
|
|
|
|
"docker info", shell=True, capture_output=True, text=True
|
|
|
|
|
)
|
|
|
|
|
if result.returncode != 0:
|
|
|
|
|
print("❌ Docker daemon is not running")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
print("✅ Docker is available and running")
|
|
|
|
|
return True
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"❌ Error checking Docker: {e}")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def start_services():
|
|
|
|
|
"""Start all development services."""
|
|
|
|
|
print("🚀 Starting development environment...")
|
|
|
|
|
|
|
|
|
|
if not check_docker():
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
# Start Docker services
|
|
|
|
|
run_command("docker-compose up -d")
|
|
|
|
|
|
|
|
|
|
# Wait for services to be ready
|
|
|
|
|
print("⏳ Waiting for services to be ready...")
|
|
|
|
|
time.sleep(10)
|
|
|
|
|
|
|
|
|
|
# Check service health
|
|
|
|
|
check_services()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def stop_services():
|
|
|
|
|
"""Stop all development services."""
|
|
|
|
|
print("🛑 Stopping development environment...")
|
|
|
|
|
run_command("docker-compose down")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def restart_services():
|
|
|
|
|
"""Restart all development services."""
|
|
|
|
|
print("🔄 Restarting development environment...")
|
|
|
|
|
stop_services()
|
|
|
|
|
start_services()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def check_services():
|
|
|
|
|
"""Check the status of all services."""
|
|
|
|
|
print("🔍 Checking service status...")
|
|
|
|
|
|
|
|
|
|
# Check Docker containers
|
|
|
|
|
result = subprocess.run(
|
|
|
|
|
"docker-compose ps", shell=True, capture_output=True, text=True
|
|
|
|
|
)
|
|
|
|
|
print(result.stdout)
|
|
|
|
|
|
|
|
|
|
# Check database connection
|
|
|
|
|
check_database()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def check_database():
|
|
|
|
|
"""Check if the database is accessible."""
|
|
|
|
|
try:
|
|
|
|
|
import psycopg2
|
|
|
|
|
from dotenv import load_dotenv
|
|
|
|
|
|
|
|
|
|
# Load environment variables
|
|
|
|
|
env_file = Path(".env")
|
|
|
|
|
if env_file.exists():
|
|
|
|
|
load_dotenv(env_file)
|
|
|
|
|
|
|
|
|
|
conn_params = {
|
|
|
|
|
"host": os.getenv("POSTGRES_HOST", "localhost"),
|
2025-05-30 18:20:38 +08:00
|
|
|
"port": os.getenv("POSTGRES_PORT", "5434"),
|
2025-05-29 23:50:41 +08:00
|
|
|
"database": os.getenv("POSTGRES_DB", "dashboard"),
|
|
|
|
|
"user": os.getenv("POSTGRES_USER", "dashboard"),
|
2025-05-30 18:20:38 +08:00
|
|
|
"password": os.getenv("POSTGRES_PASSWORD"),
|
2025-05-29 23:50:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
conn = psycopg2.connect(**conn_params)
|
|
|
|
|
conn.close()
|
|
|
|
|
print("✅ Database connection successful")
|
|
|
|
|
|
|
|
|
|
except ImportError:
|
|
|
|
|
print("⚠️ psycopg2 not installed, skipping database check")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"❌ Database connection failed: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def setup_env():
|
|
|
|
|
"""Set up environment files."""
|
|
|
|
|
print("📝 Setting up environment...")
|
|
|
|
|
|
|
|
|
|
env_file = Path(".env")
|
|
|
|
|
template_file = Path("env.template")
|
|
|
|
|
|
|
|
|
|
if not env_file.exists() and template_file.exists():
|
|
|
|
|
import shutil
|
|
|
|
|
shutil.copy(template_file, env_file)
|
|
|
|
|
print(f"✅ Created .env file from template")
|
|
|
|
|
print(f"⚠️ Please update .env with your actual configuration")
|
|
|
|
|
elif env_file.exists():
|
|
|
|
|
print("✅ .env file already exists")
|
|
|
|
|
else:
|
|
|
|
|
print("❌ No env.template file found")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def install_deps():
|
|
|
|
|
"""Install project dependencies using UV."""
|
|
|
|
|
print("📦 Installing dependencies...")
|
|
|
|
|
run_command("uv sync --dev")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def run_dev_server():
|
|
|
|
|
"""Run the development server with hot reload."""
|
|
|
|
|
print("🔥 Starting development server with hot reload...")
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
from watchdog.observers import Observer
|
|
|
|
|
from watchdog.events import FileSystemEventHandler
|
|
|
|
|
import threading
|
|
|
|
|
import signal
|
|
|
|
|
|
|
|
|
|
class ChangeHandler(FileSystemEventHandler):
|
|
|
|
|
def __init__(self, restart_callback):
|
|
|
|
|
self.restart_callback = restart_callback
|
|
|
|
|
self.last_modified = 0
|
|
|
|
|
|
|
|
|
|
def on_modified(self, event):
|
|
|
|
|
if event.is_directory:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# Only watch Python files
|
|
|
|
|
if not event.src_path.endswith('.py'):
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# Avoid rapid restarts
|
|
|
|
|
current_time = time.time()
|
|
|
|
|
if current_time - self.last_modified < 1:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
self.last_modified = current_time
|
|
|
|
|
print(f"\n📝 File changed: {event.src_path}")
|
|
|
|
|
self.restart_callback()
|
|
|
|
|
|
|
|
|
|
# Application process
|
|
|
|
|
app_process = None
|
|
|
|
|
|
|
|
|
|
def start_app():
|
|
|
|
|
nonlocal app_process
|
|
|
|
|
if app_process:
|
|
|
|
|
app_process.terminate()
|
|
|
|
|
app_process.wait()
|
|
|
|
|
|
|
|
|
|
print("🚀 Starting application...")
|
|
|
|
|
app_process = subprocess.Popen([
|
|
|
|
|
sys.executable, "main.py"
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
def restart_app():
|
|
|
|
|
print("🔄 Restarting application...")
|
|
|
|
|
start_app()
|
|
|
|
|
|
|
|
|
|
# Set up file watcher
|
|
|
|
|
event_handler = ChangeHandler(restart_app)
|
|
|
|
|
observer = Observer()
|
|
|
|
|
|
|
|
|
|
# Watch main directories
|
|
|
|
|
watch_dirs = [".", "config", "database", "components", "data", "strategies", "trader"]
|
|
|
|
|
for watch_dir in watch_dirs:
|
|
|
|
|
if Path(watch_dir).exists():
|
|
|
|
|
observer.schedule(event_handler, watch_dir, recursive=True)
|
|
|
|
|
print(f"👀 Watching: {watch_dir}/")
|
|
|
|
|
|
|
|
|
|
# Start watching
|
|
|
|
|
observer.start()
|
|
|
|
|
|
|
|
|
|
# Start initial app
|
|
|
|
|
start_app()
|
|
|
|
|
|
|
|
|
|
def signal_handler(signum, frame):
|
|
|
|
|
print("\n🛑 Shutting down development server...")
|
|
|
|
|
observer.stop()
|
|
|
|
|
if app_process:
|
|
|
|
|
app_process.terminate()
|
|
|
|
|
app_process.wait()
|
|
|
|
|
sys.exit(0)
|
|
|
|
|
|
|
|
|
|
signal.signal(signal.SIGINT, signal_handler)
|
|
|
|
|
|
|
|
|
|
print("🔥 Development server running with hot reload")
|
|
|
|
|
print("📁 Watching for changes in Python files...")
|
|
|
|
|
print("Press Ctrl+C to stop")
|
|
|
|
|
|
|
|
|
|
# Keep the main thread alive
|
|
|
|
|
observer.join()
|
|
|
|
|
|
|
|
|
|
except ImportError:
|
|
|
|
|
print("❌ watchdog not installed, running without hot reload")
|
|
|
|
|
print("Install with: uv add watchdog")
|
|
|
|
|
# Fallback to regular execution
|
|
|
|
|
run_command("uv run python main.py")
|
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
|
print("\n🛑 Development server stopped")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"❌ Error running dev server: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def run_app():
|
|
|
|
|
"""Run the application without hot reload."""
|
|
|
|
|
print("🚀 Starting application...")
|
|
|
|
|
run_command("uv run python main.py")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
"""Main entry point for the development script."""
|
|
|
|
|
if len(sys.argv) < 2:
|
|
|
|
|
print("Usage: python scripts/dev.py [command]")
|
|
|
|
|
print("Commands:")
|
|
|
|
|
print(" setup - Set up environment and install dependencies")
|
|
|
|
|
print(" start - Start development services (Docker)")
|
|
|
|
|
print(" stop - Stop development services")
|
|
|
|
|
print(" restart - Restart development services")
|
|
|
|
|
print(" status - Check service status")
|
|
|
|
|
print(" install - Install dependencies")
|
|
|
|
|
print(" dev-server - Run development server with hot reload")
|
|
|
|
|
print(" run - Run application without hot reload")
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
command = sys.argv[1]
|
|
|
|
|
|
|
|
|
|
if command == "setup":
|
|
|
|
|
setup_env()
|
|
|
|
|
install_deps()
|
|
|
|
|
elif command == "start":
|
|
|
|
|
start_services()
|
|
|
|
|
elif command == "stop":
|
|
|
|
|
stop_services()
|
|
|
|
|
elif command == "restart":
|
|
|
|
|
restart_services()
|
|
|
|
|
elif command == "status":
|
|
|
|
|
check_services()
|
|
|
|
|
elif command == "install":
|
|
|
|
|
install_deps()
|
|
|
|
|
elif command == "dev-server":
|
|
|
|
|
run_dev_server()
|
|
|
|
|
elif command == "run":
|
|
|
|
|
run_app()
|
|
|
|
|
else:
|
|
|
|
|
print(f"Unknown command: {command}")
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
main()
|