feat: arranque y bootstrap multiplataforma (macOS/Linux)
- scripts/bootstrap.sh: setup idempotente (python>=3.10, venv, extras, ffmpeg/node con hints de brew/apt, config.yaml desde example) - scripts/start-server.sh / stop-server.sh: equivalentes bash de los .ps1 (reuso de servidor via .run/server.info, puerto libre 8000-8100, healthz, apertura de navegador con open/xdg-open) - Makefile: setup/serve/stop/test/clean (Windows apunta a los .bat/.ps1) - compatible con bash 3.2 de macOS, sin bashismos
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
#!/usr/bin/env bash
|
||||
# start-server.sh — arranque del servidor web de yt-scraper en macOS/Linux.
|
||||
# Espejo de scripts/start-server.ps1 (Windows); mismo formato de .run/server.info
|
||||
# ("PUERTO PID FECHA"), asi ambos conviven sobre el mismo proyecto.
|
||||
#
|
||||
# Uso:
|
||||
# bash scripts/start-server.sh (o `make serve`)
|
||||
# PYTHON_EXE=/ruta/python3 bash scripts/start-server.sh # igual que -PythonExe en el .ps1
|
||||
#
|
||||
# Compatible con el bash 3.2 de macOS y con POSIX sh: sin arrays asociativos,
|
||||
# sin ${var,,}, sin mapfile.
|
||||
|
||||
set -u
|
||||
|
||||
# --- raiz del proyecto (carpeta padre de scripts/) ---
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" || exit 1
|
||||
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" || exit 1
|
||||
cd "$ROOT" || exit 1
|
||||
|
||||
RUN_DIR=".run"
|
||||
INFO_FILE="$RUN_DIR/server.info"
|
||||
LOG_FILE="$RUN_DIR/server.log"
|
||||
ERR_LOG_FILE="$RUN_DIR/server.err.log"
|
||||
HOST="127.0.0.1"
|
||||
PORT_MIN=8000
|
||||
PORT_MAX=8100
|
||||
READY_TIMEOUT=30 # segundos maximos esperando /healthz
|
||||
|
||||
say() { printf '%s\n' "$*"; }
|
||||
|
||||
# --- interprete Python: venv del proyecto si existe, si no python3 ---
|
||||
if [ -n "${PYTHON_EXE:-}" ]; then
|
||||
PY="$PYTHON_EXE"
|
||||
elif [ -x ".venv/bin/python" ]; then
|
||||
PY=".venv/bin/python"
|
||||
else
|
||||
PY="$(command -v python3 || command -v python || true)"
|
||||
if [ -z "$PY" ]; then
|
||||
say " [error] no encontre python3. Instalalo con: brew install [email protected]"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- GET a /healthz; exit 0 si responde 'ok' (curl, con fallback a python) ---
|
||||
healthz_ok() {
|
||||
# $1 = puerto
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsS --max-time 2 "http://$HOST:$1/healthz" 2>/dev/null | grep -q ok
|
||||
else
|
||||
"$PY" -c "import urllib.request,sys; sys.exit(0 if b'ok' in urllib.request.urlopen('http://$HOST:$1/healthz', timeout=2).read() else 1)" >/dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
# --- abre el navegador segun el SO; si no puede, imprime la URL ---
|
||||
open_browser() {
|
||||
case "$(uname)" in
|
||||
Darwin)
|
||||
if command -v open >/dev/null 2>&1; then
|
||||
open "$1" && say " [ok] navegador abierto -> $1" && return 0
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
if command -v xdg-open >/dev/null 2>&1; then
|
||||
xdg-open "$1" && say " [ok] navegador abierto -> $1" && return 0
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
say " [info] abre esta URL en tu navegador: $1"
|
||||
}
|
||||
|
||||
mkdir -p "$RUN_DIR"
|
||||
|
||||
say ""
|
||||
say " [yt-scraper] iniciando servidor local..."
|
||||
|
||||
# --- (1) servidor ya registrado y vivo? -> reutilizarlo ---
|
||||
# El puerto respondiendo es la verdad: si /healthz contesta, ese ES el
|
||||
# servidor corriendo, aunque el PID registrado haya muerto (el proceso
|
||||
# padre puede terminar y dejar el hijo vivo).
|
||||
if [ -f "$INFO_FILE" ]; then
|
||||
reg_port=""
|
||||
first_line="$(head -n 1 "$INFO_FILE" 2>/dev/null || true)"
|
||||
# primera linea: "PUERTO PID FECHA"
|
||||
set -- $first_line
|
||||
reg_port="${1:-}"
|
||||
if [ -n "$reg_port" ] && healthz_ok "$reg_port"; then
|
||||
say " [ok] servidor ya activo en puerto $reg_port"
|
||||
say " URL: http://localhost:$reg_port"
|
||||
say " log: $LOG_FILE"
|
||||
say " stop: bash scripts/stop-server.sh"
|
||||
open_browser "http://$HOST:$reg_port"
|
||||
say ""
|
||||
exit 0
|
||||
fi
|
||||
say " [info] Habia un registro viejo de otra sesion; limpiandolo..."
|
||||
rm -f "$INFO_FILE"
|
||||
fi
|
||||
|
||||
# --- (2) puertos libres en 8000-8100 (prueba de bind con Python) ---
|
||||
FREE_PORTS="$("$PY" - <<'PYEOF' 2>/dev/null
|
||||
import socket
|
||||
free = []
|
||||
for port in range(8000, 8101):
|
||||
s = socket.socket()
|
||||
try:
|
||||
s.bind(("127.0.0.1", port))
|
||||
except OSError:
|
||||
s.close()
|
||||
continue
|
||||
s.close()
|
||||
free.append(str(port))
|
||||
if len(free) >= 8:
|
||||
break
|
||||
print(" ".join(free))
|
||||
PYEOF
|
||||
)"
|
||||
if [ -z "$FREE_PORTS" ]; then
|
||||
say " [error] no se encontro ningun puerto libre ($PORT_MIN-$PORT_MAX)"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# --- (3) lanzar uvicorn en cada candidato hasta que uno responda ---
|
||||
launched=0
|
||||
for PORT in $FREE_PORTS; do
|
||||
rm -f "$LOG_FILE" "$ERR_LOG_FILE"
|
||||
say " [...] Encendiendo el servidor (intento $PORT)..."
|
||||
nohup "$PY" -m uvicorn yt_scraper.webapp.app:app \
|
||||
--host "$HOST" --port "$PORT" --log-level info \
|
||||
>>"$LOG_FILE" 2>>"$ERR_LOG_FILE" &
|
||||
SERVER_PID=$!
|
||||
say " El servidor corre en segundo plano. Registro: $LOG_FILE"
|
||||
|
||||
# Esperar /healthz (max ~30 s); si el proceso muere, probar otro puerto.
|
||||
waited=0
|
||||
ready=0
|
||||
while [ "$waited" -lt "$READY_TIMEOUT" ]; do
|
||||
if ! kill -0 "$SERVER_PID" 2>/dev/null; then
|
||||
say " [warn] el proceso murio al arrancar; probando el siguiente puerto..."
|
||||
break
|
||||
fi
|
||||
if healthz_ok "$PORT"; then
|
||||
ready=1
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
waited=$((waited + 1))
|
||||
done
|
||||
|
||||
if [ "$ready" -ne 1 ]; then
|
||||
# Mostrar el motivo probable del fallo para poder diagnosticarlo.
|
||||
if [ -f "$ERR_LOG_FILE" ]; then
|
||||
say " (ultimas lineas de $ERR_LOG_FILE):"
|
||||
tail -n 6 "$ERR_LOG_FILE" 2>/dev/null | sed 's/^/ /'
|
||||
fi
|
||||
kill "$SERVER_PID" 2>/dev/null
|
||||
continue
|
||||
fi
|
||||
|
||||
printf '%s %s %s\n' "$PORT" "$SERVER_PID" "$(date '+%Y-%m-%dT%H:%M:%S%z')" > "$INFO_FILE"
|
||||
say " [ok] Servidor encendido."
|
||||
say " URL: http://localhost:$PORT"
|
||||
say " log: $LOG_FILE"
|
||||
say " stop: bash scripts/stop-server.sh"
|
||||
open_browser "http://$HOST:$PORT"
|
||||
launched=1
|
||||
break
|
||||
done
|
||||
|
||||
if [ "$launched" -ne 1 ]; then
|
||||
say " [error] no se pudo iniciar el servidor en ninguno de los puertos candidatos."
|
||||
if [ -f "$LOG_FILE" ]; then
|
||||
say " --- ultimo log del servidor ---"
|
||||
tail -n 25 "$LOG_FILE" 2>/dev/null | sed 's/^/ /'
|
||||
else
|
||||
say ' instala dependencias: pip install -e ".[web]"'
|
||||
fi
|
||||
say ""
|
||||
exit 3
|
||||
fi
|
||||
|
||||
say ""
|
||||
exit 0
|
||||
Reference in New Issue
Block a user