diff --git a/setup.sh b/setup.sh index 463d17c..b3c5af7 100644 --- a/setup.sh +++ b/setup.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -set -euo pipefail +set -uo pipefail # ============================================================ # LiveServer-M1 — Universal Setup / Update / Doctor Script @@ -325,8 +325,8 @@ cmd_doctor() { log_ok "$cmd: $(command -v "$cmd")" else log_err "$cmd не найден" - ((issues++)) - install_if_missing "$cmd" "см. документацию" && ((fixed++)) || true + issues=$((issues + 1)) + install_if_missing "$cmd" "см. документацию" && fixed=$((fixed + 1)) || true fi done @@ -335,16 +335,16 @@ cmd_doctor() { log_ok "docker compose: $(docker compose version --short 2>/dev/null)" else log_err "docker compose V2 не найден" - ((issues++)) + issues=$((issues + 1)) fi if docker info &>/dev/null; then log_ok "Docker daemon запущен" else log_err "Docker daemon не запущен" - ((issues++)) + issues=$((issues + 1)) log_fix "Запускаю Docker..." - sudo systemctl start docker 2>/dev/null && ((fixed++)) || log_warn "Не удалось запустить Docker" + sudo systemctl start docker 2>/dev/null && fixed=$((fixed + 1)) || log_warn "Не удалось запустить Docker" fi fi @@ -352,7 +352,7 @@ cmd_doctor() { log_step "2/8 — Файл .env" if [[ ! -f .env ]]; then log_err ".env не найден" - ((issues++)) + issues=$((issues + 1)) if ask_yn "Запустить полную установку?" "y"; then cmd_install return @@ -367,7 +367,7 @@ cmd_doctor() { val=$(env_get "$var") if [[ -z "$val" ]]; then log_err "${var} не задан в .env" - ((issues++)) + issues=$((issues + 1)) fi done @@ -377,22 +377,27 @@ cmd_doctor() { pg_pass=$(env_get POSTGRES_PASSWORD) if [[ "$pg_pass" == "postgres" ]]; then log_warn "POSTGRES_PASSWORD = 'postgres' в production — небезопасно!" - ((issues++)) + issues=$((issues + 1)) fi local minio_pass minio_pass=$(env_get MINIO_ROOT_PASSWORD) if [[ "$minio_pass" == "minioadmin" ]]; then log_warn "MINIO_ROOT_PASSWORD = 'minioadmin' в production — небезопасно!" - ((issues++)) + issues=$((issues + 1)) fi fi # Проверить что .env.example не содержит новых переменных if [[ -f .env.example ]]; then local missing_in_env=0 - while IFS='=' read -r key _; do - key=$(echo "$key" | sed 's/^#\s*//' | xargs) - [[ -z "$key" || "$key" =~ ^# ]] && continue + while IFS= read -r line; do + # Пропустить пустые строки и комментарии + [[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continue + # Извлечь ключ (до первого =) + local key="${line%%=*}" + # Убрать пробелы и проверить что это валидное имя переменной + key=$(echo "$key" | tr -d '[:space:]') + [[ -z "$key" || ! "$key" =~ ^[A-Z_][A-Z0-9_]*$ ]] && continue if ! grep -qE "^${key}=" .env 2>/dev/null; then if [[ "$missing_in_env" -eq 0 ]]; then log_warn "Новые переменные из .env.example отсутствуют в .env:" @@ -401,7 +406,7 @@ cmd_doctor() { missing_in_env=1 fi done < .env.example - [[ "$missing_in_env" -eq 1 ]] && ((issues++)) + [[ "$missing_in_env" -eq 1 ]] && issues=$((issues + 1)) fi fi @@ -414,14 +419,14 @@ cmd_doctor() { type_val=$(node -e "console.log(require('./package.json').type || '')" 2>/dev/null || echo "") if [[ -n "$type_val" ]]; then log_err "package.json содержит \"type\": \"${type_val}\" — вызывает ESM/CJS конфликт с Turbopack" - ((issues++)) + issues=$((issues + 1)) log_fix "Удаляю поле \"type\" из package.json..." node -e " const fs = require('fs'); const pkg = JSON.parse(fs.readFileSync('package.json','utf8')); delete pkg.type; fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n'); - " 2>/dev/null && { log_ok "\"type\" удалён"; ((fixed++)); } || log_warn "Не удалось автоисправить" + " 2>/dev/null && { log_ok "\"type\" удалён"; fixed=$((fixed + 1)); } || log_warn "Не удалось автоисправить" fi else log_ok "package.json: нет конфликтного поля \"type\"" @@ -430,18 +435,18 @@ cmd_doctor() { # node_modules if [[ ! -d "node_modules" ]]; then log_warn "node_modules отсутствует" - ((issues++)) + issues=$((issues + 1)) log_fix "Запускаю npm install..." npm install 2>&1 | tail -5 log_ok "npm install завершён" - ((fixed++)) + fixed=$((fixed + 1)) elif [[ "package.json" -nt "node_modules/.package-lock.json" ]] 2>/dev/null; then log_warn "node_modules устарел (package.json новее)" - ((issues++)) + issues=$((issues + 1)) log_fix "Запускаю npm install..." npm install 2>&1 | tail -5 log_ok "npm install обновлён" - ((fixed++)) + fixed=$((fixed + 1)) else log_ok "node_modules актуален" fi @@ -451,14 +456,14 @@ cmd_doctor() { log_ok "Prisma Client сгенерирован" else log_warn "Prisma Client не найден" - ((issues++)) + issues=$((issues + 1)) log_fix "Генерирую Prisma Client..." npx prisma generate 2>&1 | tail -2 - ((fixed++)) + fixed=$((fixed + 1)) fi else log_err "package.json не найден — это не корень проекта?" - ((issues++)) + issues=$((issues + 1)) fi # --- 4. Docker контейнеры --- @@ -475,7 +480,7 @@ cmd_doctor() { ;; unhealthy) log_warn "${svc}: unhealthy" - ((issues++)) + issues=$((issues + 1)) log_fix "Перезапускаю ${svc}..." docker compose restart "$svc" 2>/dev/null sleep 3 @@ -483,14 +488,14 @@ cmd_doctor() { new_state=$(check_service "$svc") if [[ "$new_state" == "ok" ]]; then log_ok "${svc}: восстановлен" - ((fixed++)) + fixed=$((fixed + 1)) else log_err "${svc}: всё ещё проблема после рестарта" fi ;; *) log_err "${svc}: ${state}" - ((issues++)) + issues=$((issues + 1)) stopped_services+=("$svc") ;; esac @@ -505,7 +510,7 @@ cmd_doctor() { new_state=$(check_service "$svc") if [[ "$new_state" == "ok" ]]; then log_ok "${svc}: запущен" - ((fixed++)) + fixed=$((fixed + 1)) else log_err "${svc}: не удалось запустить" fi @@ -520,7 +525,7 @@ cmd_doctor() { log_ok "PostgreSQL: pg_isready OK" else log_err "PostgreSQL: pg_isready FAIL" - ((issues++)) + issues=$((issues + 1)) fi # Проверить что БД liveserver существует @@ -528,9 +533,9 @@ cmd_doctor() { log_ok "БД 'liveserver' существует" else log_warn "БД 'liveserver' не найдена" - ((issues++)) + issues=$((issues + 1)) log_fix "Создаю БД..." - docker compose exec -T postgres createdb -U postgres liveserver 2>/dev/null && ((fixed++)) || true + docker compose exec -T postgres createdb -U postgres liveserver 2>/dev/null && fixed=$((fixed + 1)) || true fi # Проверить схему (есть ли таблицы) @@ -540,10 +545,10 @@ cmd_doctor() { log_ok "Схема БД: ${table_count} таблиц" else log_warn "Схема БД пуста" - ((issues++)) + issues=$((issues + 1)) log_fix "Применяю Prisma schema..." - npx prisma db push --skip-generate 2>&1 | tail -3 - ((fixed++)) + npx prisma db push --skip-generate --accept-data-loss 2>&1 | tail -3 + fixed=$((fixed + 1)) fi # PgBouncer @@ -552,7 +557,7 @@ cmd_doctor() { log_ok "PgBouncer: подключение OK" else log_warn "PgBouncer: не отвечает через pg_isready" - ((issues++)) + issues=$((issues + 1)) fi fi else @@ -566,7 +571,7 @@ cmd_doctor() { log_ok "Redis PING → PONG" else log_err "Redis не отвечает на PING" - ((issues++)) + issues=$((issues + 1)) fi local redis_mem @@ -599,7 +604,7 @@ cmd_doctor() { log_ok "MinIO health: live" else log_warn "MinIO health endpoint не отвечает (может быть закрыт порт)" - ((issues++)) + issues=$((issues + 1)) fi # Проверить bucket @@ -610,14 +615,14 @@ cmd_doctor() { log_ok "MinIO: bucket '${bucket}' доступен" else log_warn "MinIO: bucket '${bucket}' недоступен (HTTP ${bucket_check})" - ((issues++)) + issues=$((issues + 1)) log_fix "Создаю bucket..." if check_command mc; then mc alias set local "${s3_endpoint}" "$minio_user" "$minio_pass" 2>/dev/null || true mc mb "local/${bucket}" 2>/dev/null || true - ((fixed++)) + fixed=$((fixed + 1)) else - curl -sf -o /dev/null -X PUT "${s3_endpoint}/${bucket}" -u "${minio_user}:${minio_pass}" 2>/dev/null && ((fixed++)) || \ + curl -sf -o /dev/null -X PUT "${s3_endpoint}/${bucket}" -u "${minio_user}:${minio_pass}" 2>/dev/null && fixed=$((fixed + 1)) || \ log_warn "Создай вручную: http://localhost:9001" fi fi @@ -637,11 +642,11 @@ cmd_doctor() { log_ok "BUILD_ID: $(cat .next/BUILD_ID)" else log_warn ".next может быть повреждён (нет BUILD_ID)" - ((issues++)) + issues=$((issues + 1)) if ask_yn "Очистить .next кеш?" "y"; then rm -rf .next log_fix "Кеш очищен" - ((fixed++)) + fixed=$((fixed + 1)) fi fi else @@ -651,7 +656,7 @@ cmd_doctor() { # Проверить lock file конфликт if [[ -f "package-lock.json" ]] && [[ -f "yarn.lock" ]]; then log_warn "Найдены И package-lock.json И yarn.lock — выбери один менеджер" - ((issues++)) + issues=$((issues + 1)) fi # --- Итого --- @@ -690,6 +695,7 @@ cmd_update() { log_info "Текущая ветка: ${branch}" # Проверить uncommitted changes + local skip_pull=false if ! git diff --quiet 2>/dev/null || ! git diff --cached --quiet 2>/dev/null; then log_warn "Есть незакоммиченные изменения" if ask_yn "Сохранить через git stash и продолжить?" "y"; then @@ -697,11 +703,14 @@ cmd_update() { log_ok "Изменения сохранены в stash" else log_warn "Пропускаю git pull" + skip_pull=true fi fi - git pull --rebase 2>&1 | tail -5 - log_ok "git pull завершён" + if [[ "$skip_pull" == "false" ]]; then + git pull --rebase 2>&1 | tail -5 + log_ok "git pull завершён" + fi else log_warn "Не git-репозиторий — пропускаю git pull" fi @@ -719,7 +728,7 @@ cmd_update() { # Проверить нужна ли миграция if docker compose exec -T postgres pg_isready -U postgres &>/dev/null; then log_info "Применяю schema changes..." - npx prisma db push --skip-generate 2>&1 | tail -3 + npx prisma db push --skip-generate --accept-data-loss 2>&1 | tail -3 log_ok "Схема БД синхронизирована" else log_warn "PostgreSQL недоступен — миграция пропущена" @@ -980,7 +989,7 @@ ENVEOF log_warn "PgBouncer не отвечает — продолжаем без него" echo -e " Применение миграций..." - npx prisma db push --skip-generate 2>&1 | tail -3 + npx prisma db push --skip-generate --accept-data-loss 2>&1 | tail -3 log_ok "Схема БД синхронизирована" echo -e " Создание S3 bucket..."