File size: 6,129 Bytes
207aa37
 
 
 
 
 
 
5fad218
207aa37
 
5fad218
207aa37
 
5fad218
 
 
 
 
 
 
 
 
 
 
 
207aa37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5fad218
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
04706d1
 
 
 
 
 
 
 
 
 
 
 
 
207aa37
 
 
 
 
 
 
 
5fad218
 
 
 
 
 
 
207aa37
 
5fad218
207aa37
5fad218
 
207aa37
5fad218
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207aa37
 
5fad218
 
 
207aa37
 
 
 
 
 
 
 
 
 
 
04706d1
207aa37
 
 
 
 
04706d1
 
 
 
 
 
 
 
 
 
 
 
 
 
207aa37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
04706d1
207aa37
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
#!/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