From 63b3472b8254305d2f427e806937612e7190c3ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=98=D0=9D?= Date: Fri, 29 May 2026 02:53:12 +0300 Subject: [PATCH 1/3] ci: add auto-deploy to S1+S2 via rsync after merge to main Adds new "deploy" job in .gitea/workflows/ci.yml that runs on push to main (after PR is merged). Builds production bundle and rsyncs it to /var/www/rublox-player/build/ on both production servers (S1 VM 124 via NAT 1998, S2 VM 124 directly via runner network). Uses Gitea Secrets: - DEPLOY_SSH_KEY: dedicated ed25519 key for CI, pubkey already on ~min/.ssh/authorized_keys on both VM 124 - KNOWN_HOSTS: host-keys of both targets to prevent MITM Also updates CONTRIBUTING.md: - Maintainer workflow section explaining why even Lead works via PR - Hotfix flow (always via PR, never direct push to main) - DevPanel as fallback if CI deploy is broken --- .gitea/workflows/ci.yml | 70 +++++++++++++++++++++++++++++++++++++++-- CONTRIBUTING.md | 53 ++++++++++++++++++++++++++++++- 2 files changed, 120 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index bb50e78..7b5564d 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -41,9 +41,11 @@ jobs: - run: npm ci - run: npm run build - name: Save build size + # set -o pipefail (default Gitea Actions) валит step при SIGPIPE + # от head. Делаем команды непадающими через || true. run: | - du -sh build/ - ls -la build/assets/ | head -10 + du -sh build/ || true + ls -la build/assets/ 2>/dev/null | head -10 || true secret-scan: name: Secret scan @@ -83,3 +85,67 @@ jobs: if [ "$TOTAL" -gt 1000 ]; then echo "::warning::PR изменяет $TOTAL строк (> 1000). Подумай о дроблении на несколько меньших." fi + + # ──────────────────────────────────────────────────────────────────── + # DEPLOY — собирает прод-бандл и заливает на ОБА сервера (S1+S2) + # параллельно через rsync over SSH. + # + # Запускается ТОЛЬКО на push в main (т.е. после успешного мержа PR). + # PR-проверки выше (lint/build/secret-scan/size-check) гарантируют + # что в main попадает только корректный код. + # + # Секреты: + # DEPLOY_SSH_KEY — приватный ed25519 ключ (CI-only, отдельный от + # админских), pubkey уже на ~min/.ssh/authorized_keys + # на S1 VM 124 и S2 VM 124 + # KNOWN_HOSTS — host-keys S1 и S2 (защита от MITM) + # + # Цели (на VM 124 обоих серверов): + # /var/www/rublox-player/build/ + # ──────────────────────────────────────────────────────────────────── + deploy: + name: Deploy to S1 + S2 + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + needs: [lint, build, secret-scan] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: '18' + - name: Install deps + run: npm ci + - name: Production build + run: npm run build + - name: Prepare SSH + env: + DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }} + KNOWN_HOSTS: ${{ secrets.KNOWN_HOSTS }} + run: | + mkdir -p ~/.ssh && chmod 700 ~/.ssh + echo "$DEPLOY_SSH_KEY" > ~/.ssh/id_deploy + chmod 600 ~/.ssh/id_deploy + echo "$KNOWN_HOSTS" > ~/.ssh/known_hosts + chmod 600 ~/.ssh/known_hosts + - name: Install rsync + run: sudo apt-get update -qq && sudo apt-get install -y rsync openssh-client + - name: Deploy to S1 (85.175.7.40:1998) + run: | + rsync -az --delete-after --human-readable \ + -e "ssh -i ~/.ssh/id_deploy -o UserKnownHostsFile=~/.ssh/known_hosts -p 1998" \ + build/ min@85.175.7.40:/var/www/rublox-player/build/ + - name: Deploy to S2 (192.168.0.124:22, runner в той же сети) + run: | + rsync -az --delete-after --human-readable \ + -e "ssh -i ~/.ssh/id_deploy -o UserKnownHostsFile=~/.ssh/known_hosts -p 22" \ + build/ min@192.168.0.124:/var/www/rublox-player/build/ + - name: Verify deploy + run: | + echo "=== S1 ===" + ssh -i ~/.ssh/id_deploy -o UserKnownHostsFile=~/.ssh/known_hosts -p 1998 \ + min@85.175.7.40 \ + "ls /var/www/rublox-player/build/index.html && du -sh /var/www/rublox-player/build/" + echo "=== S2 ===" + ssh -i ~/.ssh/id_deploy -o UserKnownHostsFile=~/.ssh/known_hosts -p 22 \ + min@192.168.0.124 \ + "ls /var/www/rublox-player/build/index.html && du -sh /var/www/rublox-player/build/" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b4df933..ce6ebae 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -122,7 +122,7 @@ Pre-commit-хук (Husky, если установлен) заблокирует - Ожидай фидбек в течение **48 часов** (часто в тот же день). - Маленькие PR (<300 строк) ревьюятся быстро. PR'ы >1000 строк попросят раздробить. - После approval **мерджит мейнтейнер** (право merge только у него). -- После merge автоматический webhook запускает деплой в прод. +- После merge **CI автоматически билдит и заливает на оба прод-сервера** (S1 и S2 параллельно через rsync). Время от мержа до прода ~3-5 мин. ## Что не смерджим @@ -140,6 +140,57 @@ Pre-commit-хук (Husky, если установлен) заблокирует - Новые API для скриптов (в `ScriptSandboxAPI.js`) - Улучшения документации (особенно с примерами) +## Для мейнтейнеров (включая Lead) + +**Даже Lead работает через PR.** Это не бюрократия — это страховка: + +- **CI запускается только на PR и push в main**, прямой коммит в main минует CI → можно положить прод. +- **История изменений линейна** — каждое изменение имеет описание, ревьюера (даже самого себя) и chain of trust. +- **Контрибьюторы видят дисциплину Lead'а** и сами начинают её соблюдать. + +### Workflow Lead/Maintainer (когда сам себе ревьюер) + +```bash +# 1. Feature-ветка от свежей main +git checkout main && git pull +git checkout -b feature/название + +# 2. Делаем изменения, коммитим, пушим +git push origin feature/название + +# 3. Открываем PR через Gitea UI или git.rublox.pro CLI +# 4. Ждём пока CI зелёный (~2 мин на lint+build) +# 5. Самоапрув + Merge (можно Squash для feature, Rebase для chore) +# 6. CI deploy job сам зальёт на S1+S2 (~3 мин) +``` + +### Когда можно прямой push в main + +**Никогда.** Даже emergency hotfix: + +```bash +git checkout -b hotfix/что-сломалось +# фикс + git commit + git push +# Открыть PR → дождаться CI зелёного (~2 мин) → merge +# CI задеплоит за 3 мин +``` + +Итого hotfix-флоу = ~5 мин от обнаружения проблемы до развёрнутого фикса. +Это быстрее чем хаотичный прямой push с риском сломать прод повторно. + +### Если CI deploy сломался (как fallback) + +Можно вручную через DevPanel: + +```bash +# На локалке +cd c:/Users/min/Desktop/server/rublox-player +npm run build +# Затем заливка через DevPanel → "rublox-player" → "Deploy" +``` + +Это **временный** обходной путь. После починки CI обязательно вернуться к авто-деплою. + ## Безопасность Нашёл уязвимость? **Не открывай публичный issue.** Пиши на `security@rublox.pro` напрямую. См. [SECURITY.md](./SECURITY.md). -- 2.47.2 From 210d26a3f8c85111f2ba40e45b89dc5eb1c811f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=98=D0=9D?= Date: Fri, 29 May 2026 04:08:56 +0300 Subject: [PATCH 2/3] ci: re-trigger checks -- 2.47.2 From a4e34efb761ff18b83d23967db12e7b66d4c8bc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=98=D0=9D?= Date: Fri, 29 May 2026 04:16:40 +0300 Subject: [PATCH 3/3] =?UTF-8?q?ci:=20=D1=83=D0=B1=D1=80=D0=B0=D1=82=D1=8C?= =?UTF-8?q?=20lint=20=D0=B8=D0=B7=20needs=20deploy-job?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Deploy зависит от lint через needs:, поэтому при failure lint (исторический долг empty-блоков) deploy не запускается ВООБЩЕ (висит pending бесконечно). Lint опциональный — не в branch protection required-checks. Deploy должен зависеть только от Build + Secret-scan. --- .gitea/workflows/ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 7b5564d..51dab3f 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -106,7 +106,10 @@ jobs: deploy: name: Deploy to S1 + S2 if: github.event_name == 'push' && github.ref == 'refs/heads/main' - needs: [lint, build, secret-scan] + # Lint НЕ в needs — он опциональный (исторический долг empty-блоков + # ещё не вычищен, см. branch protection без 'CI / Lint' в required). + # Deploy всё равно зависит от Build и Secret-scan — это критично. + needs: [build, secret-scan] runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 -- 2.47.2