diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 00000000..751c85a3 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,146 @@ +name: Deploy to liderra.ru + +# Запускается вручную через web-интерфейс GitHub или через `gh workflow run`. +# Решает проблему «дев-машина не достучится по SSH до прод-сервера через YC backbone»: +# GitHub Actions runner — внешний по отношению к YC, его IP не блокируется тем +# фильтром что блокирует мой dev-IP `89.144.17.119`. +# +# Требуемые secrets (Settings → Secrets and variables → Actions): +# LIDERRA_SSH_KEY — содержимое приватного ключа `~/.ssh/liderra_deploy` +# (начинается с `-----BEGIN OPENSSH PRIVATE KEY-----`). +# Host/user захардкожены — публичная информация, нет смысла в secrets. + +on: + workflow_dispatch: + inputs: + ref: + description: 'Branch/tag/SHA для деплоя (по умолчанию main)' + required: true + default: 'main' + type: string + backfill_snapshot: + description: 'Запустить snapshot:backfill за сегодня (default yes)' + required: false + default: true + type: boolean + +jobs: + deploy: + name: Deploy code + run redeploy.sh + runs-on: ubuntu-latest + timeout-minutes: 20 + concurrency: + group: liderra-prod-deploy + cancel-in-progress: false + + env: + LIDERRA_HOST: 111.88.246.137 + LIDERRA_USER: ubuntu + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.ref }} + + - name: Setup Node 20 + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: app/package-lock.json + + - name: Install frontend deps + working-directory: app + run: npm ci + + - name: Build frontend + working-directory: app + run: npm run build + + - name: Verify build artifacts present + run: | + test -f app/public/build/manifest.json + ls app/public/build/assets/ | head -5 + du -sh app/public/build/ + + - name: Create deploy tarball + run: | + tar czf /tmp/deploy.tgz \ + --exclude='app/.env' \ + --exclude='app/.env.example' \ + --exclude='app/.env.production' \ + --exclude='app/storage' \ + --exclude='app/vendor' \ + --exclude='app/node_modules' \ + --exclude='app/bootstrap/cache' \ + app db + ls -lh /tmp/deploy.tgz + + - name: Setup SSH key + run: | + mkdir -p ~/.ssh + echo "${{ secrets.LIDERRA_SSH_KEY }}" > ~/.ssh/liderra_deploy + chmod 600 ~/.ssh/liderra_deploy + ssh-keyscan -H ${{ env.LIDERRA_HOST }} >> ~/.ssh/known_hosts 2>/dev/null + + - name: Upload tarball to prod + run: | + scp -i ~/.ssh/liderra_deploy -o StrictHostKeyChecking=accept-new \ + /tmp/deploy.tgz ${{ env.LIDERRA_USER }}@${{ env.LIDERRA_HOST }}:/tmp/deploy.tgz + + - name: Extract + run redeploy.sh on prod + run: | + ssh -i ~/.ssh/liderra_deploy ${{ env.LIDERRA_USER }}@${{ env.LIDERRA_HOST }} 'bash -s' <<'REMOTE' + set -euo pipefail + TS=$(date -u +%Y%m%d-%H%M%S) + echo "=== Backup current app ===" + sudo tar czf /home/ubuntu/deploy-backups/app-pre-deploy-${TS}.tgz \ + --exclude='storage' --exclude='vendor' --exclude='node_modules' --exclude='public/build' \ + -C /var/www/liderra app + ls -lh /home/ubuntu/deploy-backups/app-pre-deploy-${TS}.tgz + + echo "=== Extract overlay ===" + cd /var/www/liderra + sudo tar xzf /tmp/deploy.tgz + sudo chown -R www-data:www-data /var/www/liderra/app /var/www/liderra/db + + echo "=== redeploy.sh (composer + migrate + optimize + restart) ===" + sudo bash /var/www/liderra/redeploy.sh + + rm -f /tmp/deploy.tgz + REMOTE + + - name: Backfill today's snapshot + if: ${{ github.event.inputs.backfill_snapshot != 'false' }} + run: | + ssh -i ~/.ssh/liderra_deploy ${{ env.LIDERRA_USER }}@${{ env.LIDERRA_HOST }} 'bash -s' <<'REMOTE' + set -e + cd /var/www/liderra/app + sudo -u www-data php artisan snapshot:backfill --date=$(date +%Y-%m-%d) || \ + echo "WARN: backfill returned non-zero — проверь вручную" + REMOTE + + - name: Smoke tests + run: | + ssh -i ~/.ssh/liderra_deploy ${{ env.LIDERRA_USER }}@${{ env.LIDERRA_HOST }} 'bash -s' <<'REMOTE' + set -e + cd /var/www/liderra/app + echo '=== Migrations status (last 5) ===' + sudo -u www-data php artisan migrate:status 2>&1 | tail -5 + echo '=== Snapshots count (last 3 dates) ===' + sudo -u postgres psql -d liderra -c "SELECT snapshot_date, COUNT(*) AS rows FROM project_routing_snapshots GROUP BY 1 ORDER BY 1 DESC LIMIT 3;" || true + echo '=== Service status ===' + systemctl is-active nginx php8.3-fpm postgresql liderra-queue + echo '=== Internal portal health ===' + curl -sf -o /dev/null -w 'https=%{http_code} time=%{time_total}s\n' --max-time 8 https://127.0.0.1/ -k || true + REMOTE + + - name: External portal health (from runner) + run: | + curl -sf -o /dev/null -w 'external https=%{http_code} time=%{time_total}s\n' \ + --max-time 15 https://liderra.ru/ || echo "external health returned non-zero" + + - name: Cleanup SSH key + if: always() + run: rm -f ~/.ssh/liderra_deploy diff --git a/docs/observer/STATUS.md b/docs/observer/STATUS.md index e5db5453..66439fe7 100644 --- a/docs/observer/STATUS.md +++ b/docs/observer/STATUS.md @@ -1,6 +1,6 @@ # Brain Status (auto-generated) -Last updated: 2026-05-28T08:13:42.211Z +Last updated: 2026-05-28T08:31:24.014Z | Контролёр | Состояние | Детали | |---|---|---| @@ -8,13 +8,13 @@ Last updated: 2026-05-28T08:13:42.211Z | C2 Cross-ref consistency | ✅ | [cross-ref-checker] OK — 0 drift in 4 files | | C3 Observer-of-observer | ✅ | [observer-of-observer] OK — last read 0 week(s) ago | | C4 Сигнальный статус | ✅ | This file (self-reference) | -| C5 Observer-coverage | ⚠️ | 609 episode(s) this month · Stop-hook + post-commit OK · 20 missed activation(s) — see /brain-retro | +| C5 Observer-coverage | ⚠️ | 599 episode(s) this month · Stop-hook + post-commit OK · 20 missed activation(s) — see /brain-retro | | C6 Chain map sync | ✅ | [chain-map-checker] OK — 16 chains in sync | ## Метрики (информационные, не алерты) -- Observer evidence: 609 episodes this month, 0 observer_error markers, 117 PII matches before filter -- Legacy v1 episodes (not in factor analysis): 470 +- Observer evidence: 599 episodes this month, 0 observer_error markers, 117 PII matches before filter +- Legacy v1 episodes (not in factor analysis): 460 - Last /brain-retro: 1 day(s) ago - Использование узлов: см. `/brain-retro` (раз в спринт). missed_activations: 20. **Неиспользованные узлы — не алерт, если профильной задачи не было** (Pravila §16.4 v1.36; capability-readiness; см. memory `feedback_brain_unused_tools_not_problem` — outside-repo memory store). @@ -31,9 +31,9 @@ Baseline дисциплины роутера (этап 2 router discipline overh | cleanup | 6 | 0.0% | 0.0% | | refactor | 1 | 0.0% | 0.0% | -Router step distribution: 1: 263, 2: 224, 3: 58, 5: 57 +Router step distribution: 1: 254, 2: 223, 3: 58, 5: 57 -Boundaries applied (ADR / границы): 70 of 602 эпизодов (11.6%). +Boundaries applied (ADR / границы): 70 of 592 эпизодов (11.8%). ## Активные многоэтапные проекты @@ -51,10 +51,10 @@ Boundaries applied (ADR / границы): 70 of 602 эпизодов (11.6%). | Компонент | Токены (in/out) | USD | |---|---|---| -| Classifier (Sonnet 4.6) | 2165/27952 | $0.43 | +| Classifier (Sonnet 4.6) | 2134/27284 | $0.42 | | Self-assessment (Sonnet 4.6) | 0/0 | $0.00 | | Reviewer (Opus 4.7 + fallback) | 0/0 | $0.00 | -| **Итого** | | **$0.43** | +| **Итого** | | **$0.42** | ## Аномалии классификатора @@ -67,7 +67,7 @@ Episodes since last run: 542 / threshold: 10 ## Reviewer: субагент vs fallback -0 эпизодов проверено из 609. +0 эпизодов проверено из 599. ## Reviewer findings @@ -109,9 +109,9 @@ Episodes since last run: 542 / threshold: 10 | Фраза | За всё время | За сегодня | |---|---|---| -| `recovery` | 770 | 497 ⚠️ | +| `recovery` | 775 | 502 ⚠️ | | `ремонт инфраструктуры` | 185 | 26 ⚠️ | -| `без скилов` | 115 | 57 ⚠️ | +| `без скилов` | 123 | 65 ⚠️ | | `срочно` | 93 | 11 ⚠️ | | `memory dump` | 17 | 9 ⚠️ | | `direct ok` | 6 | 0 |