Spaces:
Paused
Paused
| set -e | |
| echo "==========================================" | |
| echo " PyRunner - Starting up..." | |
| echo "==========================================" | |
| # Validate required environment variables | |
| if [ -z "$SECRET_KEY" ]; then | |
| echo "" | |
| echo "ERROR: SECRET_KEY is required but not set." | |
| echo "" | |
| echo "Generate one with:" | |
| echo " python -c \"from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())\"" | |
| echo "" | |
| exit 1 | |
| fi | |
| if [ -z "$ENCRYPTION_KEY" ]; then | |
| echo "" | |
| echo "ERROR: ENCRYPTION_KEY is required but not set." | |
| echo "" | |
| echo "Generate one with:" | |
| echo " python -c \"from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())\"" | |
| echo "" | |
| exit 1 | |
| fi | |
| # Run setup (migrations + default environment) | |
| echo "[*] Running setup..." | |
| python manage.py setup | |
| echo "" | |
| echo "[*] Starting services..." | |
| # PID file location | |
| QCLUSTER_PID_FILE="/tmp/qcluster.pid" | |
| # Function to start qcluster worker | |
| start_qcluster() { | |
| echo " - Starting django-q2 worker..." | |
| python manage.py qcluster & | |
| QCLUSTER_PID=$! | |
| echo $QCLUSTER_PID > "$QCLUSTER_PID_FILE" | |
| echo " - Worker started with PID $QCLUSTER_PID" | |
| } | |
| # Function to stop qcluster gracefully | |
| stop_qcluster() { | |
| if [ -f "$QCLUSTER_PID_FILE" ]; then | |
| local pid=$(cat "$QCLUSTER_PID_FILE") | |
| if kill -0 $pid 2>/dev/null; then | |
| echo "[*] Stopping worker (PID $pid)..." | |
| kill -TERM $pid 2>/dev/null || true | |
| # Wait up to 30 seconds for graceful shutdown | |
| local count=0 | |
| while kill -0 $pid 2>/dev/null && [ $count -lt 30 ]; do | |
| sleep 1 | |
| count=$((count + 1)) | |
| done | |
| # Force kill if still running | |
| if kill -0 $pid 2>/dev/null; then | |
| echo "[!] Worker did not stop gracefully, force killing..." | |
| kill -9 $pid 2>/dev/null || true | |
| fi | |
| fi | |
| rm -f "$QCLUSTER_PID_FILE" | |
| fi | |
| } | |
| # Signal handler for restart request (SIGUSR1) | |
| handle_restart() { | |
| echo "" | |
| echo "[*] Restart signal received, restarting workers..." | |
| stop_qcluster | |
| start_qcluster | |
| echo "[*] Workers restarted successfully" | |
| } | |
| trap handle_restart SIGUSR1 | |
| # Handle graceful shutdown | |
| cleanup() { | |
| echo "" | |
| echo "[*] Shutting down..." | |
| stop_qcluster | |
| # Kill monitor if running | |
| if [ -n "$MONITOR_PID" ]; then | |
| kill $MONITOR_PID 2>/dev/null || true | |
| fi | |
| exit 0 | |
| } | |
| trap cleanup SIGTERM SIGINT | |
| # Start workers initially | |
| start_qcluster | |
| # Worker monitoring loop (runs in background) | |
| monitor_workers() { | |
| local backoff=1 | |
| local max_backoff=60 | |
| while true; do | |
| sleep 5 | |
| # Check if worker is still running | |
| if [ -f "$QCLUSTER_PID_FILE" ]; then | |
| local pid=$(cat "$QCLUSTER_PID_FILE") | |
| if ! kill -0 $pid 2>/dev/null; then | |
| echo "[!] Worker (PID $pid) died unexpectedly, restarting in ${backoff}s..." | |
| sleep $backoff | |
| start_qcluster | |
| # Increase backoff (exponential with max) | |
| backoff=$((backoff * 2)) | |
| if [ $backoff -gt $max_backoff ]; then | |
| backoff=$max_backoff | |
| fi | |
| else | |
| # Worker is running, reset backoff | |
| backoff=1 | |
| fi | |
| fi | |
| done | |
| } | |
| monitor_workers & | |
| MONITOR_PID=$! | |
| # Start gunicorn web server | |
| echo " - Starting web server on port ${PORT:-8000}..." | |
| echo "" | |
| echo "==========================================" | |
| echo " PyRunner is ready!" | |
| echo " Open http://localhost:${PORT:-8000}" | |
| echo "==========================================" | |
| echo "" | |
| exec gunicorn pyrunner.wsgi:application \ | |
| --bind 0.0.0.0:${PORT:-8000} \ | |
| --workers ${GUNICORN_WORKERS:-2} \ | |
| --threads ${GUNICORN_THREADS:-4} \ | |
| --timeout ${GUNICORN_TIMEOUT:-120} \ | |
| --access-logfile - \ | |
| --error-logfile - | |