2026-07-08 19:19:17 +03:00
|
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
# Лидерра — ЗАЩИЩЁННЫЙ выкат фронт-сборки портала (public/build).
|
|
|
|
|
|
#
|
|
|
|
|
|
# ЗАЧЕМ. Портал выкатывается ПОЛНОЙ заменой папки public/build. Если собрать
|
|
|
|
|
|
# из УСТАРЕВШЕЙ ветки (где нет чужих свежих фич) и залить — чужая работа
|
|
|
|
|
|
# МОЛЧА затирается. Реальный инцидент 08.07.2026: выкат Яндекс.Метрики,
|
|
|
|
|
|
# собранный из ветки без колонок «Сделок», снёс колонки «Конкурент/Источник».
|
|
|
|
|
|
#
|
|
|
|
|
|
# ЧТО ДЕЛАЕТ. Перед подменой сверяет: новая сборка НЕ должна ПОТЕРЯТЬ ни одной
|
|
|
|
|
|
# критичной фичи, которая есть в живой сборке. Потеряла — ОТКАЗ, боевой не тронут.
|
|
|
|
|
|
# Осознанный откат (когда фичу убираем намеренно) — только с FORCE=1.
|
|
|
|
|
|
#
|
|
|
|
|
|
# ВАЖНО. Маркеры ищем ТОЛЬКО в чанках, на которые ссылается manifest.json
|
|
|
|
|
|
# (активная сборка), а не по всей assets/ — там копятся «осколки» старых
|
|
|
|
|
|
# overlay-выкатов, из-за которых наивная проверка промахивалась.
|
|
|
|
|
|
#
|
|
|
|
|
|
# ИСПОЛЬЗОВАНИЕ (с dev-машины):
|
|
|
|
|
|
# tar czf build.tgz -C app/public/build .
|
|
|
|
|
|
# scp build.tgz liderra:/tmp/build.tgz
|
|
|
|
|
|
# ssh liderra 'sudo bash /var/www/liderra/deploy-build.sh /tmp/build.tgz "ветка/sha/что выкатываем"'
|
|
|
|
|
|
# # только проверить, не выкатывая:
|
|
|
|
|
|
# ssh liderra 'sudo DRY_RUN=1 bash /var/www/liderra/deploy-build.sh /tmp/build.tgz "проверка"'
|
|
|
|
|
|
# # осознанный откат/удаление фичи:
|
|
|
|
|
|
# ssh liderra 'sudo FORCE=1 bash /var/www/liderra/deploy-build.sh /tmp/build.tgz "rollback: ..."'
|
|
|
|
|
|
#
|
|
|
|
|
|
# Откат в любой момент: mv build.bak-guard-<ts> build (см. вывод скрипта).
|
|
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
|
|
TGZ="${1:?укажи путь к tar.gz со сборкой (содержимое папки build)}"
|
|
|
|
|
|
INFO="${2:-}"
|
|
|
|
|
|
FORCE="${FORCE:-}"
|
|
|
|
|
|
DRY_RUN="${DRY_RUN:-}"
|
|
|
|
|
|
BASE=/var/www/liderra/app/public
|
|
|
|
|
|
LIVE="$BASE/build"
|
|
|
|
|
|
|
|
|
|
|
|
# Критичные маркеры фич: если строка есть в ЖИВОЙ сборке — обязана быть и в новой.
|
|
|
|
|
|
# ➕ Добавляй строку сюда, когда выкатываешь новую пользовательскую фичу,
|
|
|
|
|
|
# чтобы её тоже нельзя было случайно затереть следующим выкатом.
|
|
|
|
|
|
MARKERS=(
|
|
|
|
|
|
"Конкурент" # колонки «Сделок»: Конкурент / Источник
|
|
|
|
|
|
"mc.yandex" # Яндекс.Метрика (счётчик + Вебвизор)
|
|
|
|
|
|
"ym-hide-content" # маскировка ПДн лидов для Метрики (152-ФЗ) — критично
|
|
|
|
|
|
"deal-card" # мобильные/планшетные карточки сделок
|
|
|
|
|
|
"autopodbor" # автоподбор конкурентов
|
2026-07-21 12:21:40 +03:00
|
|
|
|
"warming/yandex" # прогрев кандидатов: три площадки (Яндекс/ВК/Телеграм) — экран SalesWarmingView
|
2026-07-08 19:19:17 +03:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
echo "▶ Защищённый выкат сборки портала. Инфо: ${INFO:-—}"
|
|
|
|
|
|
[ -f "$TGZ" ] || { echo "❌ Нет файла: $TGZ"; exit 2; }
|
|
|
|
|
|
|
|
|
|
|
|
STAGING="$BASE/.build-staging.$$"
|
|
|
|
|
|
rm -rf "$STAGING"; mkdir -p "$STAGING"
|
|
|
|
|
|
tar xzf "$TGZ" -C "$STAGING"
|
|
|
|
|
|
[ -f "$STAGING/manifest.json" ] || { echo "❌ В сборке нет manifest.json — это не билд портала."; rm -rf "$STAGING"; exit 2; }
|
|
|
|
|
|
|
|
|
|
|
|
# Список АКТИВНЫХ чанков (js/css) по manifest — без orphan-осколков прошлых выкатов.
|
|
|
|
|
|
active_files() { # $1 = dir сборки → печатает полные пути к активным чанкам
|
|
|
|
|
|
local dir="$1"
|
|
|
|
|
|
grep -oE 'assets/[A-Za-z0-9_.-]+\.(js|css)' "$dir/manifest.json" | sort -u | sed "s|^|$dir/|"
|
|
|
|
|
|
}
|
|
|
|
|
|
mapfile -t LIVE_FILES < <(active_files "$LIVE" 2>/dev/null || true)
|
|
|
|
|
|
mapfile -t NEW_FILES < <(active_files "$STAGING" 2>/dev/null || true)
|
|
|
|
|
|
|
|
|
|
|
|
hits() { # $1 = маркер, далее список файлов → сколько файлов содержат маркер
|
|
|
|
|
|
local m="$1"; shift
|
|
|
|
|
|
[ "$#" -eq 0 ] && { echo 0; return 0; }
|
|
|
|
|
|
# grep возвращает 1 при «не найдено»; при pipefail+set -e это рвёт скрипт —
|
|
|
|
|
|
# поэтому глушим код через "|| true", а число берём из вывода wc.
|
|
|
|
|
|
local c
|
|
|
|
|
|
c=$(grep -lF -- "$m" "$@" 2>/dev/null | wc -l) || true
|
|
|
|
|
|
echo "${c:-0}"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# ── ГЛАВНАЯ ЗАЩИТА: новая сборка не должна терять фичи живой ──────────────────
|
|
|
|
|
|
missing=()
|
|
|
|
|
|
for m in "${MARKERS[@]}"; do
|
|
|
|
|
|
live_n=$(hits "$m" "${LIVE_FILES[@]}")
|
|
|
|
|
|
new_n=$(hits "$m" "${NEW_FILES[@]}")
|
|
|
|
|
|
if [ "$live_n" -gt 0 ] && [ "$new_n" -eq 0 ]; then
|
|
|
|
|
|
missing+=("$m")
|
|
|
|
|
|
fi
|
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
|
|
if [ "${#missing[@]}" -gt 0 ] && [ "$FORCE" != "1" ]; then
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
echo "🛑 ОТКАЗ: новая сборка ТЕРЯЕТ фичи, которые есть на боевом:"
|
|
|
|
|
|
printf ' • %s\n' "${missing[@]}"
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
echo " Похоже, собрано из УСТАРЕВШЕЙ ветки (без чужой работы)."
|
|
|
|
|
|
echo " Пересобери из ветки-надмножества — где есть ВСЁ живое."
|
|
|
|
|
|
echo " Если это ОСОЗНАННЫЙ откат/удаление фичи — повтори с FORCE=1."
|
|
|
|
|
|
rm -rf "$STAGING"
|
|
|
|
|
|
exit 3
|
|
|
|
|
|
fi
|
|
|
|
|
|
if [ "${#missing[@]}" -gt 0 ]; then
|
|
|
|
|
|
echo "⚠ FORCE=1: осознанно теряем фичи (${missing[*]}) — по прямому указанию."
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
if [ "$DRY_RUN" = "1" ]; then
|
|
|
|
|
|
echo "✅ DRY_RUN: проверка пройдена, все живые фичи на месте. Выкат НЕ выполнялся."
|
|
|
|
|
|
rm -rf "$STAGING"
|
|
|
|
|
|
exit 0
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
# ── Подмена с бэкапом и отметкой о выкате ────────────────────────────────────
|
|
|
|
|
|
ts="$(date -u +%Y%m%d-%H%M%S)"
|
|
|
|
|
|
mv "$LIVE" "$BASE/build.bak-guard-$ts"
|
|
|
|
|
|
mv "$STAGING" "$LIVE"
|
|
|
|
|
|
appjs=$(grep -oE 'assets/app-[a-zA-Z0-9_-]+\.js' "$LIVE/manifest.json" | head -1)
|
|
|
|
|
|
printf '{"deployed_at":"%s","info":"%s","bundle":"%s","guard_ok":true}\n' \
|
|
|
|
|
|
"$ts" "${INFO//\"/}" "$appjs" > "$LIVE/DEPLOYED.json"
|
|
|
|
|
|
chown -R www-data:www-data "$LIVE"
|
|
|
|
|
|
echo "✅ Выкат ок. Бандл: $appjs. Бэкап прежней сборки: build.bak-guard-$ts"
|