Compare commits
2 Commits
9808c11467
...
b2c7edf46d
| Author | SHA1 | Date | |
|---|---|---|---|
| b2c7edf46d | |||
| 90134cfd2d |
@ -83,3 +83,67 @@ 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-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/"
|
||||||
|
|||||||
@ -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,57 @@ 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-player
|
||||||
|
npm run build
|
||||||
|
# Затем заливка через DevPanel → "rublox-player" → "Deploy"
|
||||||
|
```
|
||||||
|
|
||||||
|
Это **временный** обходной путь. После починки CI обязательно вернуться к авто-деплою.
|
||||||
|
|
||||||
## Безопасность
|
## Безопасность
|
||||||
|
|
||||||
Нашёл уязвимость? **Не открывай публичный issue.** Пиши на `security@rublox.pro` напрямую. См. [SECURITY.md](./SECURITY.md).
|
Нашёл уязвимость? **Не открывай публичный issue.** Пиши на `security@rublox.pro` напрямую. См. [SECURITY.md](./SECURITY.md).
|
||||||
|
|||||||
18
package-lock.json
generated
18
package-lock.json
generated
@ -7,6 +7,7 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "rublox-player",
|
"name": "rublox-player",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"hasInstallScript": true,
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babylonjs/core": "7.54.3",
|
"@babylonjs/core": "7.54.3",
|
||||||
@ -64,6 +65,7 @@
|
|||||||
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.29.0",
|
"@babel/code-frame": "^7.29.0",
|
||||||
"@babel/generator": "^7.29.0",
|
"@babel/generator": "^7.29.0",
|
||||||
@ -319,7 +321,8 @@
|
|||||||
"version": "7.54.3",
|
"version": "7.54.3",
|
||||||
"resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-7.54.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-7.54.3.tgz",
|
||||||
"integrity": "sha512-P5ncXVd8GEUJLhwloP9V0oVwQYIrvZztguVeLlvd5Rx+9aQnenKjpV8auJ6SRsUlAmNZU4pFTKzwF6o2EUfhAw==",
|
"integrity": "sha512-P5ncXVd8GEUJLhwloP9V0oVwQYIrvZztguVeLlvd5Rx+9aQnenKjpV8auJ6SRsUlAmNZU4pFTKzwF6o2EUfhAw==",
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/@babylonjs/loaders": {
|
"node_modules/@babylonjs/loaders": {
|
||||||
"version": "7.54.3",
|
"version": "7.54.3",
|
||||||
@ -1492,6 +1495,7 @@
|
|||||||
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@ -1740,6 +1744,13 @@
|
|||||||
"proxy-from-env": "^1.1.0"
|
"proxy-from-env": "^1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/babylonjs-gltf2interface": {
|
||||||
|
"version": "7.54.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-7.54.3.tgz",
|
||||||
|
"integrity": "sha512-ZAWYFyE+SOczfWT19O4e3YRkCZ5i57SiD2eK2kqc+Tow/t9X1S45xgSFNuHZff++dd5BlVIEQDSnFV+McFLSnQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
@ -1791,6 +1802,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.10.12",
|
"baseline-browser-mapping": "^2.10.12",
|
||||||
"caniuse-lite": "^1.0.30001782",
|
"caniuse-lite": "^1.0.30001782",
|
||||||
@ -2454,6 +2466,7 @@
|
|||||||
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
|
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.2.0",
|
"@eslint-community/eslint-utils": "^4.2.0",
|
||||||
"@eslint-community/regexpp": "^4.6.1",
|
"@eslint-community/regexpp": "^4.6.1",
|
||||||
@ -4265,6 +4278,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.1.0"
|
"loose-envify": "^1.1.0"
|
||||||
},
|
},
|
||||||
@ -4277,6 +4291,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||||
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.1.0",
|
"loose-envify": "^1.1.0",
|
||||||
"scheduler": "^0.23.2"
|
"scheduler": "^0.23.2"
|
||||||
@ -5136,6 +5151,7 @@
|
|||||||
"integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==",
|
"integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.21.3",
|
"esbuild": "^0.21.3",
|
||||||
"postcss": "^8.4.43",
|
"postcss": "^8.4.43",
|
||||||
|
|||||||
@ -34,7 +34,7 @@
|
|||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"lint": "eslint . --ext .js,.jsx --max-warnings 200",
|
"lint": "eslint . --max-warnings 200",
|
||||||
"format": "prettier --write \"src/**/*.{js,jsx,json,md,css}\"",
|
"format": "prettier --write \"src/**/*.{js,jsx,json,md,css}\"",
|
||||||
"format:check": "prettier --check \"src/**/*.{js,jsx,json,md,css}\"",
|
"format:check": "prettier --check \"src/**/*.{js,jsx,json,md,css}\"",
|
||||||
"fetch-assets": "node scripts/fetch-assets.js",
|
"fetch-assets": "node scripts/fetch-assets.js",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user