2026-05-26 08:02:21 +03:00
# deploy/
2026-07-09 16:17:15 +03:00
Скрипты применения обновлений на боевом портале **lk.liderra.ru ** (`liderra.ru` = лендинг).
> 🔴 **ОБНОВЛЕНО 09.07.2026:**
>
> - **Каноническая процедура выката** — [docs/superpowers/runbooks/2026-06-18-gitea-prod-deploy-pipeline.md](../docs/superpowers/runbooks/2026-06-18-gitea-prod-deploy-pipeline.md) (сборка из `main` на сервере, хирургический overlay). Этот файл — про серверный `redeploy.sh` (scp-эпоха), исторический контекст.
> - **Миграции — на ЖИВОЙ Managed PG кластер `c9q2cvtjpq3hgq6l0r96` как `crm_migrator`**, НЕ через `sudo -u postgres psql` на VM (= мёртвая rollback-копия). См. §6b канонического ранбука. `artisan migrate` тоже не годится (config закэширован, `crm_app_user` не владелец).
> - **Выкат — из ветки `main`** (с 09.07 `main` = боевой прод).
2026-05-26 08:02:21 +03:00
## redeploy.sh
Server-side половина деплоя. На боевом лежит в `/var/www/liderra/redeploy.sh`
(вне репозитория Laravel). Здесь — каноническая копия для версионирования
и аудита.
**Workflow деплоя: **
1. **Локально ** — собрать архив кода + Vite-сборку:
2026-06-23 19:50:40 +03:00
2026-05-26 08:02:21 +03:00
```bash
git archive HEAD app/ db/ | gzip > /tmp/deploy-code.tgz
tar czf /tmp/deploy-build.tgz -C app/public build/
` ``
2026-06-23 19:50:40 +03:00
2026-05-26 08:02:21 +03:00
2. **scp** обоих архивов на сервер.
3. **На сервере** — распаковать в ` /var/www/liderra/app/`, выставить владельца
` www-data:www-data`, запустить ` bash /var/www/liderra/redeploy.sh`.
**NB:** ` redeploy.sh` НЕ делает ` git pull` — он рассчитан на то, что код
уже залит scp. Если запустить без предварительного scp — будет no-op
(composer install / migrate / optimize / restart на той же кодовой базе).
**Квирк 107 (фикс встроен):** строка ` sudo -u www-data php artisan optimize`
обязательна. Без неё ` optimize` запускался от ` ubuntu` → ` bootstrap/cache/config.php`
с владельцем ` ubuntu` → php-fpm (под ` www-data`) не мог прочитать → 503 на всём
портале. Инцидент 24.05.2026 03:46 UTC, портал лежал 18 минут.
2026-06-23 19:50:40 +03:00
**Грабли composer-прав (фикс встроен, инцидент 23.06.2026):** ` vendor/` принадлежит
` www-data`, а ` redeploy.sh` бежит от ` ubuntu`. Голый ` composer install` падал
` autoload_classmap.php: Permission denied`, и из-за ` set -e` скрипт рвался ДО ` optimize`
и рестарта → новый код на диске, classmap новых классов НЕ пересобран → **прод 500**.
Фикс: ` sudo env COMPOSER_ALLOW_SUPERUSER=1 composer install …` + ` sudo chown -R www-data:www-data vendor`.
**Также:** при ручном восстановлении кэши надо пересобирать ДО рестарта php-fpm —
opcache держит старые до перезапуска (затяжной 500, пока fpm не рестартнут ПОСЛЕ кэшей).
2026-07-09 16:17:15 +03:00
**Грабли миграций crm_migrator (инцидент 19.06 + 23.06; ОБНОВЛЕНО 09.07 под Managed PG):**
таблицы принадлежат ` crm_migrator`, штатной ` .env`-ролью ` crm_app_user` НЕ альтерятся
(` must be owner`). **С переезда 26.06 живая база = Managed-кластер, НЕ VM.** Применять миграцию
ВРУЧНУЮ **под ` crm_migrator` на rw-endpoint Managed-кластера** (точный host + пароль Lockbox +
` sslmode=require` — §6b канонического ранбука) ДО ` redeploy.sh` + ` INSERT INTO migrations (migration, batch)
VALUES ('<имя_без_php>', (SELECT COALESCE(MAX(batch),0)+1 FROM migrations))`. Тогда ` artisan migrate
--force` в скрипте = no-op. ⚠️ **НЕ ` sudo -u postgres psql` на VM** — это мёртвая копия, миграция
туда живую базу не меняет. Точный рецепт — §6b канонического ранбука.
2026-06-23 19:50:40 +03:00
2026-05-26 08:02:21 +03:00
**Расхождение с боевым: ** если правится этот файл — синкать на боевой
(scp + проверка хеша). Боевой = source of truth для исполнения, репо =
source of truth для рецепта.