# Раннбук: автосинк и деплой кабинета Как устроено скрытие блока «Дополнительные опции» на странице Подписки и вся автоматика вокруг него: приватный форк, ежедневный синк с апстримом, сборка статики и выкладка на 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) && ...
...
)} )} ``` Благодаря этому будущий `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). Апстрим обычно фиксит сам, фикс прилетит следующим синком. Если критично и срочно — можно поправить зависимость руками отдельным коммитом. ## Что стоит проверять периодически - **Раз в 1–2 месяца — вкладка Actions.** GitHub отключает scheduled-триггер после ~60 дней без активности в репозитории. Один ручной `Run workflow` включает обратно. - **Бэк и фронт бота обновлять вместе.** Синк фронта и апдейт бэкенда бота — по-прежнему ручной процесс, специально не автоматизировали дальше деплоя. - **CHANGELOG апстрима перед выкаткой** — на случай breaking changes в API между бэкендом и фронтом.