Files
bedolaga-cabinet/docs/RUNBOOK.md

153 lines
9.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Раннбук: автосинк и деплой кабинета
Как устроено скрытие блока «Дополнительные опции» на странице Подписки и вся
автоматика вокруг него: приватный форк, ежедневный синк с апстримом, сборка
статики и выкладка на VPS.
Репозиторий: `chillpadclub/bedolaga-cabinet` (приватный форк
`BEDOLAGA-DEV/bedolaga-cabinet`).
## Суть задачи
На странице `Подписка` нужно было скрыть блок «Дополнительные опции» (докупка
устройств, трафика, управление серверами), не потеряв возможность и дальше
получать обновления апстрима — проект активно развивается, жить на статичном
форке было бы риском самим по себе.
Решение: приватный форк со своим патчем поверх + GitHub Action, который каждый
день сам подтягивает апстрим, накатывает патч и собирает готовую статику для
VPS.
## Патч: как спрятан блок
Файл — `src/pages/Subscription.tsx`. Условие рендера обёрнуто в `false &&`, но
не правкой существующей строки, а **обёрткой снаружи** — так, чтобы ни одна
оригинальная строка апстрима не менялась, только добавлены две строки до и две
после:
```tsx
{/* Additional Options — disabled on purpose */}
{false && (
<>
{subscription && // ← строка апстрима не тронута
(subscription.is_active || subscription.is_limited) &&
...
<div>...</div>
)}
</>
)}
```
Благодаря этому будущий `git merge` с апстримом конфликтует, только если
апстрим правит саму первую или последнюю строку блока — а не любую строку
внутри (докупка устройств, трафика и т.д.), которую вы всё равно не
используете.
Компоненты `DeviceTopupSheet`, `DeviceReductionSheet`, `TrafficTopupSheet`,
`ServerManagementSheet` остаются в коде и в сборке — просто не рендерятся. Это
осознанный выбор в пользу простоты патча, а не баг.
## Как работает автоматика
Один workflow — `.github/workflows/sync-and-build.yml`. Запускается по
расписанию (ежедневно, 06:00 UTC) и вручную кнопкой `Run workflow`.
1. **Merge апстрима**`git fetch` + `merge upstream/main` поверх форка.
2. **Чистка CI** — удаляет `dependabot.yml` и чужие workflow, если merge их
принёс.
3. **Push в форк** — только если merge реально что-то изменил.
4. **Сборка**`npm ci` + `npm run build:docker` (те же build-args, что и в
официальном образе).
5. **Релиз**`tar.gz` статики → GitHub Release с тегом `vX.Y.Z`.
6. **Уведомление** — сообщение в Telegram: готово или упало.
Если апстрим правит ровно ту же строку, что и патч, — шаг merge падает
**намеренно**. Чужой код не должен домёрживаться в обход человека. Приходит
сообщение в Telegram, конфликт разрешается руками локально, затем push — синк
продолжается.
Сборка и релиз происходят, только если merge реально что-то поменял (или при
ручном запуске) — вхолостую по расписанию ничего не собирается и не
публикуется.
## Файлы
| Путь | Назначение |
|---|---|
| `.github/workflows/sync-and-build.yml` | Весь пайплайн: синк, чистка, сборка, релиз, уведомления |
| `scripts/deploy-cabinet.sh` | Деплой на VPS: скачивает релиз, бэкапит текущую статику, раскатывает |
| `src/pages/Subscription.tsx` | Патч — скрытый блок «Дополнительные опции» |
| `docs/RUNBOOK.md` | Этот файл |
Удалено из форка сознательно (было в апстриме, плодило шум):
| Файл | Почему убран |
|---|---|
| `.github/dependabot.yml` | Открывал десятки PR на бамп версий — оставили только Dependabot alerts (без авто-PR) |
| `ci.yml`, `lint.yml` | Тесты/линт апстрима — код уже проверен в апстриме до мёржа |
| `codeql.yml`, `security-audit.yml` | Security-сканы апстрима, дублируют Dependabot alerts |
| `docker.yml` | Пуш официального образа в GHCR — не нужен, мы публикуем статику |
| `release-please.yml`, `release.yml` | Автобамп версии апстрима — версию читаем из `package.json` |
## Секреты и токены
| Что | Где хранится | Права |
|---|---|---|
| `TG_BOT_TOKEN` | GitHub → Settings → Secrets and variables → Actions | Отправка сообщений ботом |
| `TG_CHAT_ID` | Там же | — |
| Fine-grained PAT | VPS: `~/.cabinet-deploy-token`, права файла `600` | Только `Contents: Read-only` на этот репозиторий |
Токен для VPS специально ограничен одним репозиторием и правом только на
чтение — даже если файл утечёт, им нельзя ничего запушить или изменить.
## Как выкатить на прод
На VPS:
```bash
./deploy-cabinet.sh # последний релиз
./deploy-cabinet.sh v1.63.0 # откат/установка конкретной версии
```
Скрипт сам:
1. скачивает `cabinet-dist.tar.gz` через `gh release download`;
2. бэкапит текущую статику в `cabinet-dist.bak`;
3. раскатывает новую через `rsync -a --delete` (чистит устаревшие
хэшированные JS-чанки, не трогает `50x.html`).
Бэкап один, перезаписывается при каждом запуске. Откатиться на конкретную
версию надёжнее через `./deploy-cabinet.sh vX.Y.Z` (GitHub хранит все
релизы), а не полагаться только на `.bak`.
## Если что-то пошло не так
**Пришло Telegram-сообщение «⚠️ упал»** — почти всегда конфликт merge с
апстримом. Локально:
```bash
git fetch upstream main && git merge upstream/main
# разрешить конфликт (скорее всего в месте патча Subscription.tsx)
git add -A && git commit && git push origin main
```
Следующий прогон workflow снова пойдёт сам.
**Деплой на VPS выкатил битую сборку**:
```bash
rsync -a --delete cabinet-dist.bak/ cabinet-dist/ # мгновенный откат
# либо
./deploy-cabinet.sh vX.Y.Z # конкретный рабочий релиз
```
**Security-алерт в GitHub про уязвимость** — это Dependabot alerts (без
авто-PR). Апстрим обычно фиксит сам, фикс прилетит следующим синком. Если
критично и срочно — можно поправить зависимость руками отдельным коммитом.
## Что стоит проверять периодически
- **Раз в 12 месяца — вкладка Actions.** GitHub отключает scheduled-триггер
после ~60 дней без активности в репозитории. Один ручной `Run workflow`
включает обратно.
- **Бэк и фронт бота обновлять вместе.** Синк фронта и апдейт бэкенда бота —
по-прежнему ручной процесс, специально не автоматизировали дальше деплоя.
- **CHANGELOG апстрима перед выкаткой** — на случай breaking changes в API
между бэкендом и фронтом.