From d25ff668f7818e9d7b05b5c2e53a671e18ab1277 Mon Sep 17 00:00:00 2001 From: bot-backend Date: Wed, 19 Aug 2026 08:39:01 +0000 Subject: [PATCH] =?UTF-8?q?ci:=20=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BD=D1=84=D0=B8=D0=B3=D0=B0=20=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D0=BA=D1=81=D0=B8=20=D0=B1=D0=BE=D0=BB=D1=8C=D1=88=D0=B5=20?= =?UTF-8?q?=D0=BD=D0=B5=20=D1=82=D1=8F=D0=BD=D0=B5=D1=82=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=BB=D0=BD=D1=8B=D0=B9=20=D0=B4=D0=B5=D0=BF=D0=BB=D0=BE=D0=B9?= =?UTF-8?q?=20Site=20Finder=20(#2916)=20(#2925)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .forgejo/workflows/deploy.yml | 125 +++++++++++++++++++++++++++++----- 1 file changed, 108 insertions(+), 17 deletions(-) diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml index e61bee58..c463a0d2 100644 --- a/.forgejo/workflows/deploy.yml +++ b/.forgejo/workflows/deploy.yml @@ -51,6 +51,12 @@ jobs: backend: ${{ steps.filter.outputs.backend }} frontend: ${{ steps.filter.outputs.frontend }} infra: ${{ steps.filter.outputs.infra }} + # #2916: правка ТОЛЬКО конфига прокси. `infra` для этого не годится — он + # включает и compose, и сам workflow, где полный деплой обязателен. + # `github.event_name == 'push'` первым множителем НАМЕРЕННО: на + # workflow_dispatch у paths-filter нет диффа, и любой его ответ не должен + # уметь отключить сборку — ручной прогон обязан оставаться полным. + caddy_only: ${{ github.event_name == 'push' && steps.filter.outputs.caddy == 'true' && steps.filter.outputs.non_caddy == 'false' }} steps: - uses: actions/checkout@v4 - uses: dorny/paths-filter@v3 @@ -67,14 +73,28 @@ jobs: - 'Caddyfile' - 'caddy/**' - '.forgejo/workflows/deploy.yml' + # Пара фильтров для «правка ТОЛЬКО прокси» (#2916). Одного `caddy` + # мало: он true и когда вместе с конфигом приехал бэкенд — тогда + # нужен обычный полный деплой. `non_caddy` матчит ВСЁ остальное, + # и быстрый путь включается лишь когда он false. + caddy: + - 'Caddyfile' + - 'caddy/**' + non_caddy: + - '**' + - '!Caddyfile' + - '!caddy/**' build-backend: runs-on: ubuntu-latest needs: changes if: | - needs.changes.outputs.backend == 'true' || - needs.changes.outputs.infra == 'true' || - github.event_name == 'workflow_dispatch' + needs.changes.outputs.caddy_only != 'true' && + ( + needs.changes.outputs.backend == 'true' || + needs.changes.outputs.infra == 'true' || + github.event_name == 'workflow_dispatch' + ) steps: - uses: actions/checkout@v4 @@ -189,9 +209,12 @@ jobs: runs-on: ubuntu-latest needs: changes if: | - needs.changes.outputs.backend == 'true' || - needs.changes.outputs.infra == 'true' || - github.event_name == 'workflow_dispatch' + needs.changes.outputs.caddy_only != 'true' && + ( + needs.changes.outputs.backend == 'true' || + needs.changes.outputs.infra == 'true' || + github.event_name == 'workflow_dispatch' + ) steps: - uses: actions/checkout@v4 @@ -296,9 +319,12 @@ jobs: runs-on: ubuntu-latest needs: changes if: | - needs.changes.outputs.frontend == 'true' || - needs.changes.outputs.infra == 'true' || - github.event_name == 'workflow_dispatch' + needs.changes.outputs.caddy_only != 'true' && + ( + needs.changes.outputs.frontend == 'true' || + needs.changes.outputs.infra == 'true' || + github.event_name == 'workflow_dispatch' + ) steps: - uses: actions/checkout@v4 @@ -409,6 +435,7 @@ jobs: if: | always() && !cancelled() && + needs.changes.outputs.caddy_only != 'true' && needs.build-backend.result != 'failure' && needs.build-worker.result != 'failure' && needs.build-frontend.result != 'failure' @@ -749,6 +776,55 @@ jobs: # если deploy не завершился success — неважно, пропущен он (build упал) или # упал сам (SSH/миграция/health-check). Красная точка встаёт именно там, где # решение реально принято, а не там, где она случайно оказалась по цепочке if. + # ── Быстрый путь: правка ТОЛЬКО конфига прокси (#2916) ──────────────────── + # + # ЗАЧЕМ. `Caddyfile` лежит в фильтре `infra`, поэтому правка одной строки + # allowlist'а ради meraocenka.ru запускала полный деплой Site Finder: + # пересборку трёх образов, `git reset --hard` на боевой VM, применение ВСЕХ + # pending `data/sql/*.sql` в боевой БД gendsgn и `--force-recreate` бэкенда, + # воркера, beat и caddy. То есть радиус поражения правки, относящейся к + # чужому домену, — gendsgn.ru целиком, включая миграции продукта, который + # никто в этот момент катить не собирался. + # + # Публичный периметр МЕРЫ живёт в этом файле и будет меняться часто: новая + # страница = новая строка allowlist'а. + # + # ПОЧЕМУ `reload`, А НЕ `up -d --force-recreate caddy`. Полный деплой + # осознанно пересоздаёт контейнер (комментарий в ci.yml: `reload` отказался бы + # принять битый конфиг и оставил бы работать старый — на общем деплое это + # скрыло бы поломку). Здесь наоборот: правится ТОЛЬКО конфиг, и отказ + # применить битый — ровно то, что нужно. `caddy reload` возвращает ненулевой + # код → job краснеет, а домены продолжают обслуживаться старым конфигом. + # Альтернатива (`--force-recreate`) на опечатке уводит контейнер в crash-loop + # и роняет ВСЕ домены сразу. + # + # Гейт `caddy validate` на PR (#2913) остаётся первой линией; этот шаг — + # вторая, уже против боевого файла после `git reset`. + deploy-caddy: + runs-on: ubuntu-latest + needs: changes + # Только push: на workflow_dispatch человек просит полный деплой, и + # подменять его перезагрузкой конфига нельзя. + if: github.event_name == 'push' && needs.changes.outputs.caddy_only == 'true' + steps: + - name: Синхронизировать конфиг и перезагрузить прокси + uses: appleboy/ssh-action@v1.0.3 + with: + host: ${{ secrets.DEPLOY_HOST }} + username: ${{ secrets.DEPLOY_USER }} + key: ${{ secrets.DEPLOY_SSH_KEY }} + port: ${{ secrets.DEPLOY_PORT }} + script: | + set -euo pipefail + cd /opt/gendesign + git fetch origin main + git reset --hard origin/main + # Конфиг примонтирован read-only с хоста, пересборка не нужна — + # контейнер читает тот же файл, что только что обновил git. + docker compose -p gendesign -f docker-compose.prod.yml exec -T caddy \ + caddy reload --config /etc/caddy/Caddyfile --adapter caddyfile + echo "✓ конфиг прокси перезагружен без пересборки и без миграций" + # ── Смоук публичного периметра МЕРЫ после выкатки (#2917) ────────────────── # # ЗАЧЕМ ЗДЕСЬ. scripts/smoke-mera-perimeter.sh — единственная проверка, которая @@ -769,10 +845,15 @@ jobs: # осознанно вместо зависимости, которая может молча не сработать. perimeter-smoke: runs-on: ubuntu-latest - needs: deploy + needs: [deploy, deploy-caddy] # Только после РЕАЛЬНОЙ выкатки: при skipped/failed проверять нечего, а # красный смоук поверх несостоявшегося деплоя увёл бы разбор не туда. - if: always() && needs.deploy.result == 'success' + # ЛЮБОЙ из двух путей (#2916): быстрый путь трогает как раз конфиг прокси, + # то есть ровно то, что смоук и проверяет — пропустить его там было бы + # хуже всего. + if: | + always() && + (needs.deploy.result == 'success' || needs.deploy-caddy.result == 'success') timeout-minutes: 6 steps: - uses: actions/checkout@v4 @@ -806,19 +887,29 @@ jobs: deploy-status: runs-on: ubuntu-latest - needs: [build-backend, build-worker, build-frontend, deploy] + needs: [build-backend, build-worker, build-frontend, deploy, deploy-caddy] if: always() && !cancelled() steps: - - name: Итог прогона — деплой обязан быть success, не skipped/failure + - name: Итог прогона — выкатка обязана быть success, не skipped/failure + # #2916: путей выкатки теперь ДВА — полный деплой и быстрая перезагрузка + # конфига прокси. Успешен прогон, если сработал ЛЮБОЙ из них; ошибка — + # когда не сработал ни один. Требовать `deploy == success` как раньше + # значило бы красить каждую правку прокси, которая как раз прошла. run: | echo "build-backend: ${{ needs.build-backend.result }}" echo "build-worker: ${{ needs.build-worker.result }}" echo "build-frontend: ${{ needs.build-frontend.result }}" echo "deploy: ${{ needs.deploy.result }}" - if [ "${{ needs.deploy.result }}" != "success" ]; then - echo "::error::деплой НЕ прошёл (deploy.result=${{ needs.deploy.result }})." \ + echo "deploy-caddy: ${{ needs.deploy-caddy.result }}" + if [ "${{ needs.deploy.result }}" = "success" ]; then + echo "✓ полный деплой прошёл успешно" + elif [ "${{ needs.deploy-caddy.result }}" = "success" ]; then + echo "✓ конфиг прокси перезагружен (быстрый путь, без пересборки и миграций)" + else + echo "::error::выкатка НЕ прошла ни одним путём" \ + "(deploy=${{ needs.deploy.result }}," \ + "deploy-caddy=${{ needs.deploy-caddy.result }})." \ "Прогон должен читаться как FAILED, а не как пропущенный шаг (#2841)." \ - "Смотри логи build-backend/build-worker/build-frontend/deploy выше." + "Смотри логи build-* / deploy / deploy-caddy выше." exit 1 fi - echo "✓ деплой прошёл успешно"