ci: ������ ��������� trufflehog � secret-scan (���� ���������� deploy) #36

Merged
min merged 2 commits from fix/ci-trufflehog-retry into main 2026-06-20 16:38:21 +00:00
Showing only changes of commit 32e8efee15 - Show all commits

View File

@ -1,158 +1,168 @@
# CI плеера Рублокса. # CI плеера Рублокса.
# Запускается на каждый push и pull_request. # Запускается на каждый push и pull_request.
# #
# Что проверяем: # Что проверяем:
# 1. lint — ESLint без warning'ов # 1. lint — ESLint без warning'ов
# 2. format-check — Prettier формат не нарушен # 2. format-check — Prettier формат не нарушен
# 3. build — vite build проходит без ошибок # 3. build — vite build проходит без ошибок
# 4. secret-scan — trufflehog не нашёл утечек секретов # 4. secret-scan — trufflehog не нашёл утечек секретов
# 5. size-check — PR не больше 1000 строк (предупреждение) # 5. size-check — PR не больше 1000 строк (предупреждение)
name: CI name: CI
on: on:
push: push:
branches: [main] branches: [main]
pull_request: pull_request:
branches: [main] branches: [main]
jobs: jobs:
lint: lint:
name: Lint name: Lint
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
node-version: '18' node-version: '18'
- run: npm ci - run: npm ci
# format:check временно отключён до массового npx prettier --write # format:check временно отключён до массового npx prettier --write
# (см. docs/ONBOARDING.md → «Форматирование кода»). После прогона # (см. docs/ONBOARDING.md → «Форматирование кода»). После прогона
# верни строку `- run: npm run format:check` перед npm run lint. # верни строку `- run: npm run format:check` перед npm run lint.
- run: npm run lint - run: npm run lint
build: build:
name: Build name: Build
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
node-version: '18' node-version: '18'
- run: npm ci - run: npm ci
- run: npm run build - run: npm run build
- name: Save build size - name: Save build size
# set -o pipefail (default Gitea Actions) валит step при SIGPIPE # set -o pipefail (default Gitea Actions) валит step при SIGPIPE
# от head. Делаем команды непадающими через || true. # от head. Делаем команды непадающими через || true.
run: | run: |
du -sh build/ || true du -sh build/ || true
ls -la build/assets/ 2>/dev/null | head -10 || true ls -la build/assets/ 2>/dev/null | head -10 || true
secret-scan: secret-scan:
name: Secret scan name: Secret scan
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Install trufflehog - name: Install trufflehog
run: | run: |
curl -sSfL https://raw.githubusercontent.com/trufflesecurity/trufflehog/main/scripts/install.sh \ # Скачивание install.sh с raw.githubusercontent периодически
| sh -s -- -b /usr/local/bin # отваливается по сетевому таймауту (curl exit 28) и валит весь
- name: Run trufflehog # secret-scan → deploy skipped. Ретраим 3 раза с паузой, чтобы
run: | # разовый сетевой сбой CI не блокировал деплой.
trufflehog git "file://$(pwd)" \ for i in 1 2 3; do
--only-verified --fail \ curl -sSfL --connect-timeout 15 --max-time 120 \
--exclude-paths .trufflehog-ignore 2>&1 | tee scan.log || EXIT=$? https://raw.githubusercontent.com/trufflesecurity/trufflehog/main/scripts/install.sh \
if [ -n "$EXIT" ] && [ "$EXIT" -ne 0 ]; then | sh -s -- -b /usr/local/bin && break
echo "::error::Найдены секреты в коммитах! См. лог выше." echo "Попытка $i установить trufflehog не удалась, повтор через 10с…"
exit 1 sleep 10
fi done
command -v trufflehog
size-check: - name: Run trufflehog
name: PR size check run: |
if: github.event_name == 'pull_request' trufflehog git "file://$(pwd)" \
runs-on: ubuntu-latest --only-verified --fail \
steps: --exclude-paths .trufflehog-ignore 2>&1 | tee scan.log || EXIT=$?
- uses: actions/checkout@v3 if [ -n "$EXIT" ] && [ "$EXIT" -ne 0 ]; then
with: echo "::error::Найдены секреты в коммитах! См. лог выше."
fetch-depth: 0 exit 1
- name: Check PR size fi
run: |
ADDED=$(git diff origin/${{ github.base_ref }}...HEAD --shortstat | grep -oE '[0-9]+ insertion' | grep -oE '[0-9]+' || echo 0) size-check:
REMOVED=$(git diff origin/${{ github.base_ref }}...HEAD --shortstat | grep -oE '[0-9]+ deletion' | grep -oE '[0-9]+' || echo 0) name: PR size check
TOTAL=$((ADDED + REMOVED)) if: github.event_name == 'pull_request'
echo "PR изменяет $TOTAL строк (+$ADDED / -$REMOVED)" runs-on: ubuntu-latest
if [ "$TOTAL" -gt 1000 ]; then steps:
echo "::warning::PR изменяет $TOTAL строк (> 1000). Подумай о дроблении на несколько меньших." - uses: actions/checkout@v3
fi with:
fetch-depth: 0
# ──────────────────────────────────────────────────────────────────── - name: Check PR size
# DEPLOY — собирает прод-бандл и заливает на ОБА сервера (S1+S2) run: |
# параллельно через rsync over SSH. ADDED=$(git diff origin/${{ github.base_ref }}...HEAD --shortstat | grep -oE '[0-9]+ insertion' | grep -oE '[0-9]+' || echo 0)
# REMOVED=$(git diff origin/${{ github.base_ref }}...HEAD --shortstat | grep -oE '[0-9]+ deletion' | grep -oE '[0-9]+' || echo 0)
# Запускается ТОЛЬКО на push в main (т.е. после успешного мержа PR). TOTAL=$((ADDED + REMOVED))
# PR-проверки выше (lint/build/secret-scan/size-check) гарантируют echo "PR изменяет $TOTAL строк (+$ADDED / -$REMOVED)"
# что в main попадает только корректный код. if [ "$TOTAL" -gt 1000 ]; then
# echo "::warning::PR изменяет $TOTAL строк (> 1000). Подумай о дроблении на несколько меньших."
# Секреты: fi
# DEPLOY_SSH_KEY — приватный ed25519 ключ (CI-only, отдельный от
# админских), pubkey уже на ~min/.ssh/authorized_keys # ────────────────────────────────────────────────────────────────────
# на S1 VM 124 и S2 VM 124 # DEPLOY — собирает прод-бандл и заливает на ОБА сервера (S1+S2)
# KNOWN_HOSTS — host-keys S1 и S2 (защита от MITM) # параллельно через rsync over SSH.
# #
# Цели (на VM 124 обоих серверов): # Запускается ТОЛЬКО на push в main (т.е. после успешного мержа PR).
# /var/www/rublox-player/build/ # PR-проверки выше (lint/build/secret-scan/size-check) гарантируют
# ──────────────────────────────────────────────────────────────────── # что в main попадает только корректный код.
deploy: #
name: Deploy to S1 + S2 # Секреты:
if: github.event_name == 'push' && github.ref == 'refs/heads/main' # DEPLOY_SSH_KEY — приватный ed25519 ключ (CI-only, отдельный от
# Lint НЕ в needs — он опциональный (исторический долг empty-блоков # админских), pubkey уже на ~min/.ssh/authorized_keys
# ещё не вычищен, см. branch protection без 'CI / Lint' в required). # на S1 VM 124 и S2 VM 124
# Deploy всё равно зависит от Build и Secret-scan — это критично. # KNOWN_HOSTS — host-keys S1 и S2 (защита от MITM)
needs: [build, secret-scan] #
runs-on: ubuntu-latest # Цели (на VM 124 обоих серверов):
steps: # /var/www/rublox-player/build/
- uses: actions/checkout@v3 # ────────────────────────────────────────────────────────────────────
- uses: actions/setup-node@v3 deploy:
with: name: Deploy to S1 + S2
node-version: '18' if: github.event_name == 'push' && github.ref == 'refs/heads/main'
- name: Install deps # Lint НЕ в needs — он опциональный (исторический долг empty-блоков
run: npm ci # ещё не вычищен, см. branch protection без 'CI / Lint' в required).
- name: Production build # Deploy всё равно зависит от Build и Secret-scan — это критично.
run: npm run build needs: [build, secret-scan]
- name: Prepare SSH runs-on: ubuntu-latest
env: steps:
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }} - uses: actions/checkout@v3
KNOWN_HOSTS: ${{ secrets.KNOWN_HOSTS }} - uses: actions/setup-node@v3
run: | with:
mkdir -p ~/.ssh && chmod 700 ~/.ssh node-version: '18'
echo "$DEPLOY_SSH_KEY" > ~/.ssh/id_deploy - name: Install deps
chmod 600 ~/.ssh/id_deploy run: npm ci
echo "$KNOWN_HOSTS" > ~/.ssh/known_hosts - name: Production build
chmod 600 ~/.ssh/known_hosts run: npm run build
- name: Install rsync - name: Prepare SSH
run: apt-get update -qq && apt-get install -y rsync openssh-client env:
# S1 — НЕ блокирующий: при недоступности S1 (downtime) деплой не должен DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
# валиться, главное доставить на S2. ConnectTimeout 20с чтобы не висеть. KNOWN_HOSTS: ${{ secrets.KNOWN_HOSTS }}
- name: Deploy to S1 (85.175.7.40:1998) run: |
continue-on-error: true mkdir -p ~/.ssh && chmod 700 ~/.ssh
run: | echo "$DEPLOY_SSH_KEY" > ~/.ssh/id_deploy
rsync -az --delete-after --human-readable --exclude=wiki --exclude=kubikon-assets \ chmod 600 ~/.ssh/id_deploy
-e "ssh -i ~/.ssh/id_deploy -o UserKnownHostsFile=~/.ssh/known_hosts -o ConnectTimeout=20 -p 1998" \ echo "$KNOWN_HOSTS" > ~/.ssh/known_hosts
build/ min@85.175.7.40:/var/www/rublox-player/build/ chmod 600 ~/.ssh/known_hosts
- name: Deploy to S2 (192.168.0.124:22, runner в той же сети) - name: Install rsync
run: | run: apt-get update -qq && apt-get install -y rsync openssh-client
rsync -az --delete-after --human-readable --exclude=wiki --exclude=kubikon-assets \ # S1 — НЕ блокирующий: при недоступности S1 (downtime) деплой не должен
-e "ssh -i ~/.ssh/id_deploy -o UserKnownHostsFile=~/.ssh/known_hosts -p 22" \ # валиться, главное доставить на S2. ConnectTimeout 20с чтобы не висеть.
build/ min@192.168.0.124:/var/www/rublox-player/build/ - name: Deploy to S1 (85.175.7.40:1998)
- name: Verify S1 (не блокирующий) continue-on-error: true
continue-on-error: true run: |
run: | rsync -az --delete-after --human-readable --exclude=wiki --exclude=kubikon-assets \
ssh -i ~/.ssh/id_deploy -o UserKnownHostsFile=~/.ssh/known_hosts -o ConnectTimeout=20 -p 1998 \ -e "ssh -i ~/.ssh/id_deploy -o UserKnownHostsFile=~/.ssh/known_hosts -o ConnectTimeout=20 -p 1998" \
min@85.175.7.40 \ build/ min@85.175.7.40:/var/www/rublox-player/build/
"ls /var/www/rublox-player/build/index.html && du -sh /var/www/rublox-player/build/ 2>/dev/null || true" - name: Deploy to S2 (192.168.0.124:22, runner в той же сети)
- name: Verify S2 (обязательный) run: |
run: | rsync -az --delete-after --human-readable --exclude=wiki --exclude=kubikon-assets \
ssh -i ~/.ssh/id_deploy -o UserKnownHostsFile=~/.ssh/known_hosts -p 22 \ -e "ssh -i ~/.ssh/id_deploy -o UserKnownHostsFile=~/.ssh/known_hosts -p 22" \
min@192.168.0.124 \ build/ min@192.168.0.124:/var/www/rublox-player/build/
"ls /var/www/rublox-player/build/index.html && (du -sh /var/www/rublox-player/build/ 2>/dev/null || true)" - name: Verify S1 (не блокирующий)
continue-on-error: true
run: |
ssh -i ~/.ssh/id_deploy -o UserKnownHostsFile=~/.ssh/known_hosts -o ConnectTimeout=20 -p 1998 \
min@85.175.7.40 \
"ls /var/www/rublox-player/build/index.html && du -sh /var/www/rublox-player/build/ 2>/dev/null || true"
- name: Verify S2 (обязательный)
run: |
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/ 2>/dev/null || true)"