ci: add auto-deploy to S1+S2 via rsync after merge to main
Some checks failed
CI / Lint (pull_request) Failing after 1m8s
CI / Build (pull_request) Successful in 2m10s
CI / Secret scan (pull_request) Successful in 2m35s
CI / PR size check (pull_request) Successful in 7s
CI / Deploy to S1 + S2 (pull_request) Has been skipped

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-studio/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
This commit is contained in:
МИН 2026-05-29 02:53:02 +03:00
parent d087ed6f8e
commit 75a463232c
2 changed files with 120 additions and 1 deletions

View File

@ -83,3 +83,70 @@ jobs:
if [ "$TOTAL" -gt 1000 ]; then if [ "$TOTAL" -gt 1000 ]; then
echo "::warning::PR изменяет $TOTAL строк (> 1000). Подумай о дроблении на несколько меньших." echo "::warning::PR изменяет $TOTAL строк (> 1000). Подумай о дроблении на несколько меньших."
fi 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-studio/build/
#
# При сбое деплоя ни один сервер не остаётся в полу-задеплоенном
# состоянии: rsync делает атомарную замену с --delete-after.
# ────────────────────────────────────────────────────────────────────
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-studio/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-studio/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-studio/build/index.html && du -sh /var/www/rublox-studio/build/"
echo "=== S2 ==="
ssh -i ~/.ssh/id_deploy -o UserKnownHostsFile=~/.ssh/known_hosts -p 22 \
min@192.168.0.124 \
"ls /var/www/rublox-studio/build/index.html && du -sh /var/www/rublox-studio/build/"

View File

@ -122,7 +122,7 @@ Pre-commit-хук (Husky, если установлен) заблокирует
- Ожидай фидбек в течение **48 часов** (часто в тот же день). - Ожидай фидбек в течение **48 часов** (часто в тот же день).
- Маленькие PR (<300 строк) ревьюятся быстро. PR'ы >1000 строк попросят раздробить. - Маленькие PR (<300 строк) ревьюятся быстро. PR'ы >1000 строк попросят раздробить.
- После approval **мерджит мейнтейнер** (право merge только у него). - После approval **мерджит мейнтейнер** (право merge только у него).
- После merge автоматический webhook запускает деплой в прод. - После merge **CI автоматически билдит и заливает на оба прод-сервера** (S1 и S2 параллельно через rsync). Время от мержа до прода ~3-5 мин.
## Что не смерджим ## Что не смерджим
@ -140,6 +140,58 @@ Pre-commit-хук (Husky, если установлен) заблокирует
- Новые API для скриптов (в `ScriptSandboxAPI.js`) - Новые 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-studio
npm run build
# Затем заливка через DevPanel → "rublox-studio" → "Deploy"
# (он использует frontend_deploy.py который тоже бьёт по обоим серверам)
```
Это **временный** обходной путь. После починки CI обязательно вернуться к авто-деплою.
## Безопасность ## Безопасность
Нашёл уязвимость? **Не открывай публичный issue.** Пиши на `security@rublox.pro` напрямую. См. [SECURITY.md](./SECURITY.md). Нашёл уязвимость? **Не открывай публичный issue.** Пиши на `security@rublox.pro` напрямую. См. [SECURITY.md](./SECURITY.md).