Spaces:
Running
Running
| set -euo pipefail | |
| ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
| BACKEND_DIR="$ROOT_DIR/backend" | |
| FRONTEND_DIR="$ROOT_DIR/frontend" | |
| BACKEND_VENV="${BACKEND_VENV:-}" | |
| BACKEND_PORT="${BACKEND_PORT:-8000}" | |
| FRONTEND_PORT="${FRONTEND_PORT:-5173}" | |
| PYTHON_BIN="${PYTHON_BIN:-}" | |
| BACKEND_PID="" | |
| is_supported_python_version() { | |
| local version="$1" | |
| case "$version" in | |
| 3.10|3.11|3.12|3.13) | |
| return 0 | |
| ;; | |
| *) | |
| return 1 | |
| ;; | |
| esac | |
| } | |
| cleanup() { | |
| if [[ -n "$BACKEND_PID" ]] && kill -0 "$BACKEND_PID" >/dev/null 2>&1; then | |
| echo "" | |
| echo "Encerrando backend..." | |
| kill "$BACKEND_PID" >/dev/null 2>&1 || true | |
| wait "$BACKEND_PID" 2>/dev/null || true | |
| fi | |
| } | |
| trap cleanup EXIT INT TERM | |
| require_command() { | |
| local command_name="$1" | |
| if ! command -v "$command_name" >/dev/null 2>&1; then | |
| echo "Comando obrigatório não encontrado: $command_name" >&2 | |
| exit 1 | |
| fi | |
| } | |
| python_bin_works() { | |
| local command_name="$1" | |
| [[ -n "$command_name" ]] && command -v "$command_name" >/dev/null 2>&1 | |
| } | |
| python_version_of_bin() { | |
| local command_name="$1" | |
| if python_bin_works "$command_name"; then | |
| "$command_name" -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")' | |
| fi | |
| } | |
| venv_python_works() { | |
| local venv_dir="$1" | |
| [[ -x "$venv_dir/bin/python" ]] && "$venv_dir/bin/python" -c 'import sys' >/dev/null 2>&1 | |
| } | |
| resolve_python_bin() { | |
| local candidates=() | |
| if [[ -n "$PYTHON_BIN" ]]; then | |
| candidates+=("$PYTHON_BIN") | |
| fi | |
| candidates+=("python3.13" "python3.12" "python3.11" "python3.10" "python3" "python") | |
| local candidate | |
| for candidate in "${candidates[@]}"; do | |
| if python_bin_works "$candidate"; then | |
| local version | |
| version="$(python_version_of_bin "$candidate")" | |
| if is_supported_python_version "$version"; then | |
| echo "$candidate" | |
| return 0 | |
| fi | |
| fi | |
| done | |
| return 1 | |
| } | |
| resolve_backend_venv() { | |
| if [[ -n "$BACKEND_VENV" ]]; then | |
| echo "$BACKEND_VENV" | |
| return 0 | |
| fi | |
| local candidates=( | |
| "$BACKEND_DIR/.venv313" | |
| "$BACKEND_DIR/.venv312" | |
| "$BACKEND_DIR/.venv" | |
| ) | |
| local candidate | |
| for candidate in "${candidates[@]}"; do | |
| if venv_python_works "$candidate"; then | |
| local version | |
| version="$(python_version_of_venv "$candidate")" | |
| if is_supported_python_version "$version"; then | |
| echo "$candidate" | |
| return 0 | |
| fi | |
| fi | |
| done | |
| if python_bin_works "python3.13"; then | |
| echo "$BACKEND_DIR/.venv313" | |
| return 0 | |
| fi | |
| if python_bin_works "python3.12"; then | |
| echo "$BACKEND_DIR/.venv312" | |
| return 0 | |
| fi | |
| echo "$BACKEND_DIR/.venv" | |
| } | |
| port_in_use() { | |
| local port="$1" | |
| lsof -nP -iTCP:"$port" -sTCP:LISTEN >/dev/null 2>&1 | |
| } | |
| pick_available_port() { | |
| local port="$1" | |
| while port_in_use "$port"; do | |
| port=$((port + 1)) | |
| done | |
| echo "$port" | |
| } | |
| python_version_of_venv() { | |
| local venv_dir="$1" | |
| if [[ -x "$venv_dir/bin/python" ]]; then | |
| "$venv_dir/bin/python" -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")' | |
| fi | |
| } | |
| ensure_backend_env() { | |
| BACKEND_VENV="$(resolve_backend_venv)" | |
| PYTHON_BIN="$(resolve_python_bin || true)" | |
| if [[ -z "$PYTHON_BIN" ]]; then | |
| echo "Comando obrigatório não encontrado: python3.13, python3.12, python3.11, python3.10, python3 ou python" >&2 | |
| exit 1 | |
| fi | |
| local desired_version | |
| desired_version="$(python_version_of_bin "$PYTHON_BIN")" | |
| if [[ -d "$BACKEND_VENV" ]] && venv_python_works "$BACKEND_VENV" && [[ "$(python_version_of_venv "$BACKEND_VENV")" == "$desired_version" ]]; then | |
| echo "Usando ambiente virtual existente em $BACKEND_VENV." | |
| else | |
| if [[ -d "$BACKEND_VENV" ]]; then | |
| local current_version | |
| current_version="$(python_version_of_venv "$BACKEND_VENV")" | |
| echo "A venv em $BACKEND_VENV não está adequada para o ambiente atual." >&2 | |
| if [[ -n "$current_version" ]]; then | |
| echo "Ela usa Python $current_version, mas o script vai usar Python $desired_version." >&2 | |
| fi | |
| echo "Vou recriar a venv com $PYTHON_BIN (Python $desired_version)." >&2 | |
| rm -rf "$BACKEND_VENV" | |
| else | |
| echo "Criando ambiente virtual do backend com $PYTHON_BIN..." | |
| fi | |
| "$PYTHON_BIN" -m venv "$BACKEND_VENV" | |
| fi | |
| if [[ -d "$BACKEND_VENV" ]] && ! venv_python_works "$BACKEND_VENV"; then | |
| local current_version | |
| current_version="$(python_version_of_venv "$BACKEND_VENV")" | |
| echo "A venv em $BACKEND_VENV continua indisponível após a preparação." >&2 | |
| echo "Versão detectada: ${current_version:-desconhecida}" >&2 | |
| exit 1 | |
| fi | |
| echo "Instalando/verificando dependências do backend..." | |
| # shellcheck disable=SC1091 | |
| source "$BACKEND_VENV/bin/activate" | |
| pip install -r "$BACKEND_DIR/requirements.txt" >/dev/null | |
| } | |
| ensure_frontend_env() { | |
| require_command npm | |
| if [[ ! -d "$FRONTEND_DIR/node_modules" ]] || [[ ! -x "$FRONTEND_DIR/node_modules/.bin/vite" ]]; then | |
| echo "Instalando dependências do frontend..." | |
| (cd "$FRONTEND_DIR" && npm install) | |
| fi | |
| } | |
| prepare_ports() { | |
| if port_in_use "$BACKEND_PORT"; then | |
| local requested_port="$BACKEND_PORT" | |
| BACKEND_PORT="$(pick_available_port "$BACKEND_PORT")" | |
| echo "Porta $requested_port já está em uso no backend. Usando $BACKEND_PORT." | |
| fi | |
| if port_in_use "$FRONTEND_PORT"; then | |
| local requested_port="$FRONTEND_PORT" | |
| FRONTEND_PORT="$(pick_available_port "$FRONTEND_PORT")" | |
| echo "Porta $requested_port já está em uso no frontend. Usando $FRONTEND_PORT." | |
| fi | |
| } | |
| start_backend() { | |
| echo "Subindo backend em http://127.0.0.1:$BACKEND_PORT ..." | |
| ( | |
| cd "$BACKEND_DIR" | |
| # shellcheck disable=SC1091 | |
| source "$BACKEND_VENV/bin/activate" | |
| uvicorn app.main:app --reload --host 127.0.0.1 --port "$BACKEND_PORT" | |
| ) & | |
| BACKEND_PID=$! | |
| } | |
| start_frontend() { | |
| echo "Subindo frontend em http://127.0.0.1:$FRONTEND_PORT ..." | |
| cd "$FRONTEND_DIR" | |
| VITE_API_BASE_URL="http://127.0.0.1:$BACKEND_PORT" npm run dev -- --host 127.0.0.1 --port "$FRONTEND_PORT" | |
| } | |
| echo "Preparando ambiente de desenvolvimento..." | |
| ensure_backend_env | |
| ensure_frontend_env | |
| prepare_ports | |
| start_backend | |
| start_frontend | |