fix: setup.sh doctor/update crash fixes

- Remove set -e (((issues++)) returns exit 1 when 0 → kills script)
- Fix .env.example parser: skip comments, validate var names with regex
- Fix update: skip git pull when user declines stash (was running anyway)
- Add --accept-data-loss to prisma db push (prevents interactive hang)
- Replace ((var++)) with var=$((var + 1)) for safety with pipefail

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-24 02:44:27 +03:00
parent f44d4e8c03
commit 3b6c666744

101
setup.sh
View File

@@ -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..."