#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" WSCRAPER_DIR="$ROOT_DIR/bin/wscraper" RUNTIME_DIR="$ROOT_DIR/.runtime/wscraper-service" VENV_DIR="$RUNTIME_DIR/.venv" PID_FILE="$RUNTIME_DIR/wscraper-service.pid" LOG_FILE="$RUNTIME_DIR/wscraper-service.log" MARKER_FILE="$RUNTIME_DIR/.setup-complete" MODE="" SKIP_INSTALL="false" RESTART_WSCRAPER="false" SKIP_FETCH="false" UPDATE_WSCRAPER="false" usage() { cat <<'EOF' Usage: ./scripts/bootstrap.sh --dev-mode [--skip-wscraper-fetch] [--update-wscraper] [--skip-wscraper-install] [--restart-wscraper] ./scripts/bootstrap.sh --prod-mode [--skip-wscraper-fetch] [--update-wscraper] [--skip-wscraper-install] [--restart-wscraper] EOF } while [[ $# -gt 0 ]]; do case "$1" in --dev-mode) MODE="dev" ;; --prod-mode) MODE="prod" ;; --skip-wscraper-install) SKIP_INSTALL="true" ;; --skip-wscraper-fetch) SKIP_FETCH="true" ;; --update-wscraper) UPDATE_WSCRAPER="true" ;; --restart-wscraper|--restart) RESTART_WSCRAPER="true" ;; -h|--help) usage exit 0 ;; *) echo "Unknown option: $1" >&2 usage exit 1 ;; esac shift done if [[ -z "$MODE" ]]; then echo "--dev-mode veya --prod-mode zorunlu." >&2 usage exit 1 fi mkdir -p "$RUNTIME_DIR" if [[ -f "$ROOT_DIR/.env" ]]; then set -a # shellcheck disable=SC1091 source "$ROOT_DIR/.env" set +a fi WSCRAPER_SERVICE_HOST="${WSCRAPER_SERVICE_HOST:-0.0.0.0}" WSCRAPER_SERVICE_PORT="${WSCRAPER_SERVICE_PORT:-8787}" WSCRAPER_SERVICE_TOKEN="${WSCRAPER_SERVICE_TOKEN:-}" WSCRAPER_SERVICE_PYTHON_BIN="${WSCRAPER_SERVICE_PYTHON_BIN:-}" WSCRAPER_GIT_URL="${WSCRAPER_GIT_URL:-https://github.com/wisecolt/Bookmark-Tracker.git}" WSCRAPER_GIT_REF="${WSCRAPER_GIT_REF:-main}" detect_python_bin() { if [[ -n "$WSCRAPER_SERVICE_PYTHON_BIN" ]] && command -v "$WSCRAPER_SERVICE_PYTHON_BIN" >/dev/null 2>&1; then echo "$WSCRAPER_SERVICE_PYTHON_BIN" return fi local candidate for candidate in python3.12 python3.11 python3.10 python3; do if ! command -v "$candidate" >/dev/null 2>&1; then continue fi if "$candidate" -c 'import sys; raise SystemExit(0 if sys.version_info >= (3, 10) else 1)' >/dev/null 2>&1; then echo "$candidate" return fi done echo "q-buffer watcher icin Python 3.10+ gerekli." >&2 exit 1 } PYTHON_BIN="$(detect_python_bin)" ensure_wscraper_repo() { if [[ "$SKIP_FETCH" == "true" ]]; then echo "wscraper repo kontrolu atlandi." return fi if [[ ! -d "$WSCRAPER_DIR" ]]; then echo "wscraper repo klonlaniyor..." git clone --branch "$WSCRAPER_GIT_REF" "$WSCRAPER_GIT_URL" "$WSCRAPER_DIR" return fi if [[ ! -d "$WSCRAPER_DIR/.git" ]]; then echo "bin/wscraper dizini var ama git reposu degil. Lutfen duzeltin veya dizini temizleyin." >&2 exit 1 fi if [[ "$UPDATE_WSCRAPER" != "true" ]]; then echo "wscraper repo mevcut, yeniden klonlanmayacak." return fi local status_output status_output="$(git -C "$WSCRAPER_DIR" status --porcelain)" if [[ -n "$status_output" ]]; then echo "wscraper repo kirli durumda; --update-wscraper uygulanmadi." >&2 echo "Lutfen bin/wscraper icindeki degisiklikleri commit edin veya temizleyin." >&2 exit 1 fi echo "wscraper repo guncelleniyor..." git -C "$WSCRAPER_DIR" pull --ff-only } validate_wscraper_repo() { local expected_paths=( "$WSCRAPER_DIR/pyproject.toml" "$WSCRAPER_DIR/setup.py" "$WSCRAPER_DIR/src/wscraper/cli.py" ) local path for path in "${expected_paths[@]}"; do if [[ ! -e "$path" ]]; then echo "wscraper repo eksik veya bozuk gorunuyor: $path bulunamadi." >&2 exit 1 fi done } service_running() { if [[ ! -f "$PID_FILE" ]]; then return 1 fi local pid pid="$(cat "$PID_FILE")" if [[ -z "$pid" ]]; then return 1 fi if kill -0 "$pid" >/dev/null 2>&1; then return 0 fi rm -f "$PID_FILE" return 1 } install_wscraper_service() { if [[ "$SKIP_INSTALL" == "true" ]]; then echo "wscraper kurulum kontrolu atlandi." return fi local needs_install="false" if [[ ! -x "$VENV_DIR/bin/python3" ]]; then needs_install="true" elif [[ ! -f "$MARKER_FILE" ]]; then needs_install="true" elif ! "$VENV_DIR/bin/python3" -c "import scrapling" >/dev/null 2>&1; then needs_install="true" fi if [[ "$needs_install" == "false" ]]; then echo "wscraper kurulumu mevcut, tekrar kurulmayacak." return fi echo "wscraper host servisi kuruluyor..." rm -rf "$VENV_DIR" "$PYTHON_BIN" -m venv "$VENV_DIR" "$VENV_DIR/bin/pip" install --upgrade pip "$VENV_DIR/bin/pip" install "scrapling[fetchers]==0.4.1" "$VENV_DIR/bin/scrapling" install touch "$MARKER_FILE" } stop_wscraper_service() { if ! service_running; then return fi local pid pid="$(cat "$PID_FILE")" echo "wscraper-service durduruluyor (pid=$pid)..." kill "$pid" >/dev/null 2>&1 || true rm -f "$PID_FILE" } start_wscraper_service() { if service_running && [[ "$RESTART_WSCRAPER" != "true" ]]; then echo "wscraper-service zaten calisiyor." return fi if [[ "$RESTART_WSCRAPER" == "true" ]]; then stop_wscraper_service fi echo "wscraper-service baslatiliyor..." nohup env \ WSCRAPER_SERVICE_HOST="$WSCRAPER_SERVICE_HOST" \ WSCRAPER_SERVICE_PORT="$WSCRAPER_SERVICE_PORT" \ WSCRAPER_SERVICE_TOKEN="$WSCRAPER_SERVICE_TOKEN" \ PYTHONPATH="$WSCRAPER_DIR/src" \ "$VENV_DIR/bin/python3" \ "$ROOT_DIR/bin/wscraper-service/server.py" \ >>"$LOG_FILE" 2>&1 & echo $! > "$PID_FILE" local health_url="http://127.0.0.1:${WSCRAPER_SERVICE_PORT}/health" for _ in $(seq 1 20); do if [[ -n "$WSCRAPER_SERVICE_TOKEN" ]]; then if curl -fsS -H "Authorization: Bearer ${WSCRAPER_SERVICE_TOKEN}" "$health_url" >/dev/null 2>&1; then echo "wscraper-service hazir." return fi elif curl -fsS "$health_url" >/dev/null 2>&1; then echo "wscraper-service hazir." return fi sleep 1 done echo "wscraper-service health-check basarisiz. Log: $LOG_FILE" >&2 exit 1 } run_docker() { if [[ "$MODE" == "dev" ]]; then docker compose -f "$ROOT_DIR/docker-compose.dev.yml" up --build return fi docker compose -f "$ROOT_DIR/docker-compose.yml" up --build -d } ensure_wscraper_repo validate_wscraper_repo install_wscraper_service start_wscraper_service run_docker