siprac / dev.sh
Guilherme Silberfarb Costa
Ajusta dev.sh e ignora venvs locais
5fad218
#!/usr/bin/env bash
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