ci: смоук периметра МЕРЫ запускается сразу после деплоя (#2917) #2922
5 changed files with 158 additions and 0 deletions
|
|
@ -113,6 +113,36 @@ jobs:
|
|||
python3 scripts/check-migration-lock-timeout.py --selftest
|
||||
python3 scripts/check-migration-lock-timeout.py
|
||||
|
||||
- name: "Guard: shell-скрипты синтаксически валидны (#2917)"
|
||||
# Соседям по этому job'у (caddy validate, lock_timeout) — тот же довод:
|
||||
# дёшево, на каждом PR, ловит опечатку до прода.
|
||||
#
|
||||
# ЗАЧЕМ ИМЕННО ЭТО. scripts/smoke-mera-perimeter.sh — единственная
|
||||
# проверка, которая видит публичный периметр МЕРЫ целиком, и до этого
|
||||
# PR она запускалась только ночным cron'ом. Опечатка в ней обнаружилась
|
||||
# бы следующим утром — и выглядела бы как регресс периметра, а не как
|
||||
# сломанный скрипт. Ни один линтер шелла в репозитории не стоит
|
||||
# (shellcheck нет), поэтому берём то, что есть в каждом образе: `bash -n`
|
||||
# разбирает файл, не исполняя его.
|
||||
#
|
||||
# ГРАНИЦА: `bash -n` ловит СИНТАКСИС, а не смысл — неверный URL или
|
||||
# перепутанный ожидаемый код он не увидит. Это не замена прогона,
|
||||
# а защита от того, что скрипт вообще не запустится.
|
||||
run: |
|
||||
set -euo pipefail
|
||||
found=0
|
||||
for f in $(git ls-files 'scripts/*.sh' 'ops/*.sh' 'ops/**/*.sh'); do
|
||||
found=$((found + 1))
|
||||
bash -n "$f" || { echo "::error file=$f::синтаксическая ошибка в shell-скрипте"; exit 1; }
|
||||
done
|
||||
# Ноль файлов означал бы, что гейт молча ничего не проверяет —
|
||||
# ровно тот случай, когда зелёный шаг не значит ничего (#2871).
|
||||
if [ "$found" -eq 0 ]; then
|
||||
echo "::error::не найдено ни одного .sh — гейт бы прошёл впустую, проверь маску"
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ синтаксис проверен у $found shell-скриптов"
|
||||
|
||||
- uses: dorny/paths-filter@v3
|
||||
id: filter
|
||||
with:
|
||||
|
|
|
|||
|
|
@ -1121,6 +1121,61 @@ jobs:
|
|||
# неважно, пропущен он (test/build упали) или упал сам (SSH/миграция/
|
||||
# health-check/сверка образов #2679). Красная точка встаёт именно там, где
|
||||
# решение реально принято, а не там, где она случайно оказалась по цепочке if.
|
||||
# ── Смоук публичного периметра МЕРЫ после выкатки (#2917) ──────────────────
|
||||
#
|
||||
# ЗАЧЕМ ЗДЕСЬ. scripts/smoke-mera-perimeter.sh — единственная проверка, которая
|
||||
# видит периметр целиком (короткие адреса, 301 с длинных, публичный API,
|
||||
# закрытость B2B-путей на публичном домене). До этого PR он запускался только
|
||||
# по cron'у 06:17 UTC, то есть регресс жил до суток и находил его либо ночной
|
||||
# прогон, либо владелец. Для правки, чья логика живёт в конфиге прокси, это
|
||||
# единственный настоящий гейт — и он был асинхронным.
|
||||
#
|
||||
# ПОЧЕМУ ОТДЕЛЬНЫЙ JOB, А НЕ ШАГ В deploy. Вердикты разные: «выкатили» и
|
||||
# «периметр цел» — два разных факта, и красный смоук не должен читаться как
|
||||
# неудавшийся деплой. Деплой к этому моменту уже прошёл; смоук говорит, что
|
||||
# именно получилось.
|
||||
#
|
||||
# ПОЧЕМУ ДУБЛИРУЕТСЯ В ДВУХ ПАЙПЛАЙНАХ. Конфиг прокси (deploy.yml) и фронт
|
||||
# МЕРЫ (deploy-tradein.yml) едут раздельно, и сломать периметр может каждый.
|
||||
# `workflow_call` под act_runner не гарантирован, поэтому 20 строк повторены
|
||||
# осознанно вместо зависимости, которая может молча не сработать.
|
||||
perimeter-smoke:
|
||||
runs-on: ubuntu-latest
|
||||
needs: deploy
|
||||
# Только после РЕАЛЬНОЙ выкатки: при skipped/failed проверять нечего, а
|
||||
# красный смоук поверх несостоявшегося деплоя увёл бы разбор не туда.
|
||||
if: always() && needs.deploy.result == 'success'
|
||||
timeout-minutes: 6
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Дождаться, пока периметр отвечает после пересоздания контейнеров
|
||||
# `up -d --force-recreate` возвращает управление раньше, чем бэкенд
|
||||
# начинает отвечать. Без ожидания смоук ловил бы не регресс, а гонку.
|
||||
# Ждём ДВА признака: лэндинг (Caddy + фронт) и API (бэкенд поднялся) —
|
||||
# одного мало, Caddy отвечает раньше апстрима.
|
||||
run: |
|
||||
set -uo pipefail
|
||||
for i in $(seq 1 30); do
|
||||
page=$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 https://meraocenka.ru/ || true)
|
||||
api=$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 https://gendsgn.ru/trade-in/api/v1/me || true)
|
||||
if [ "$page" = "200" ] && [ "$api" = "401" ]; then
|
||||
echo "периметр отвечает (попытка $i): лэндинг $page, API $api"
|
||||
exit 0
|
||||
fi
|
||||
echo "ждём готовности, попытка $i/30: лэндинг '${page:-нет ответа}', API '${api:-нет ответа}'"
|
||||
sleep 5
|
||||
done
|
||||
# НЕ падаем здесь: вердикт должен вынести смоук, а не таймаут ожидания.
|
||||
# Иначе «не успел подняться» и «периметр сломан» слились бы в один
|
||||
# красный шаг без разбора.
|
||||
echo "::warning::за 150 с периметр так и не ответил ожидаемо — запускаем смоук, его вывод и будет диагнозом"
|
||||
|
||||
- name: Смоук периметра
|
||||
run: |
|
||||
chmod +x scripts/smoke-mera-perimeter.sh
|
||||
./scripts/smoke-mera-perimeter.sh
|
||||
|
||||
deploy-status:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [test, build-backend, build-frontend, build-browser, deploy]
|
||||
|
|
|
|||
|
|
@ -749,6 +749,61 @@ jobs:
|
|||
# если deploy не завершился success — неважно, пропущен он (build упал) или
|
||||
# упал сам (SSH/миграция/health-check). Красная точка встаёт именно там, где
|
||||
# решение реально принято, а не там, где она случайно оказалась по цепочке if.
|
||||
# ── Смоук публичного периметра МЕРЫ после выкатки (#2917) ──────────────────
|
||||
#
|
||||
# ЗАЧЕМ ЗДЕСЬ. scripts/smoke-mera-perimeter.sh — единственная проверка, которая
|
||||
# видит периметр целиком (короткие адреса, 301 с длинных, публичный API,
|
||||
# закрытость B2B-путей на публичном домене). До этого PR он запускался только
|
||||
# по cron'у 06:17 UTC, то есть регресс жил до суток и находил его либо ночной
|
||||
# прогон, либо владелец. Для правки, чья логика живёт в конфиге прокси, это
|
||||
# единственный настоящий гейт — и он был асинхронным.
|
||||
#
|
||||
# ПОЧЕМУ ОТДЕЛЬНЫЙ JOB, А НЕ ШАГ В deploy. Вердикты разные: «выкатили» и
|
||||
# «периметр цел» — два разных факта, и красный смоук не должен читаться как
|
||||
# неудавшийся деплой. Деплой к этому моменту уже прошёл; смоук говорит, что
|
||||
# именно получилось.
|
||||
#
|
||||
# ПОЧЕМУ ДУБЛИРУЕТСЯ В ДВУХ ПАЙПЛАЙНАХ. Конфиг прокси (deploy.yml) и фронт
|
||||
# МЕРЫ (deploy-tradein.yml) едут раздельно, и сломать периметр может каждый.
|
||||
# `workflow_call` под act_runner не гарантирован, поэтому 20 строк повторены
|
||||
# осознанно вместо зависимости, которая может молча не сработать.
|
||||
perimeter-smoke:
|
||||
runs-on: ubuntu-latest
|
||||
needs: deploy
|
||||
# Только после РЕАЛЬНОЙ выкатки: при skipped/failed проверять нечего, а
|
||||
# красный смоук поверх несостоявшегося деплоя увёл бы разбор не туда.
|
||||
if: always() && needs.deploy.result == 'success'
|
||||
timeout-minutes: 6
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Дождаться, пока периметр отвечает после пересоздания контейнеров
|
||||
# `up -d --force-recreate` возвращает управление раньше, чем бэкенд
|
||||
# начинает отвечать. Без ожидания смоук ловил бы не регресс, а гонку.
|
||||
# Ждём ДВА признака: лэндинг (Caddy + фронт) и API (бэкенд поднялся) —
|
||||
# одного мало, Caddy отвечает раньше апстрима.
|
||||
run: |
|
||||
set -uo pipefail
|
||||
for i in $(seq 1 30); do
|
||||
page=$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 https://meraocenka.ru/ || true)
|
||||
api=$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 https://gendsgn.ru/trade-in/api/v1/me || true)
|
||||
if [ "$page" = "200" ] && [ "$api" = "401" ]; then
|
||||
echo "периметр отвечает (попытка $i): лэндинг $page, API $api"
|
||||
exit 0
|
||||
fi
|
||||
echo "ждём готовности, попытка $i/30: лэндинг '${page:-нет ответа}', API '${api:-нет ответа}'"
|
||||
sleep 5
|
||||
done
|
||||
# НЕ падаем здесь: вердикт должен вынести смоук, а не таймаут ожидания.
|
||||
# Иначе «не успел подняться» и «периметр сломан» слились бы в один
|
||||
# красный шаг без разбора.
|
||||
echo "::warning::за 150 с периметр так и не ответил ожидаемо — запускаем смоук, его вывод и будет диагнозом"
|
||||
|
||||
- name: Смоук периметра
|
||||
run: |
|
||||
chmod +x scripts/smoke-mera-perimeter.sh
|
||||
./scripts/smoke-mera-perimeter.sh
|
||||
|
||||
deploy-status:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-backend, build-worker, build-frontend, deploy]
|
||||
|
|
|
|||
|
|
@ -18,6 +18,15 @@ on:
|
|||
schedule:
|
||||
# Раз в сутки, 06:17 UTC — вне пиков, время произвольное.
|
||||
- cron: '17 6 * * *'
|
||||
# #2917: правка самого смоука должна проверяться сразу, а не следующим утром.
|
||||
# Проверки read-only (curl по публичным адресам), поэтому прогонять их на
|
||||
# push в main безопасно и дёшево. Синтаксис скрипта отдельно гейтится в
|
||||
# ci.yml на каждом PR — здесь проверяется уже поведение против прода.
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'scripts/smoke-mera-perimeter.sh'
|
||||
- '.forgejo/workflows/perimeter-smoke.yml'
|
||||
|
||||
concurrency:
|
||||
group: perimeter-smoke-mera
|
||||
|
|
|
|||
|
|
@ -121,6 +121,15 @@ check "meraocenka.ru/_next/image — must 404 (не открываем опти
|
|||
# `/trade-in/api/*`. Зелёная только первая = API открыт целиком и тест это
|
||||
# пропустил (ровно та ошибка, ради которой в Caddyfile выбран отдельный
|
||||
# префикс, а не поимённый проброс v1-путей).
|
||||
# ПРО ВНЕШНЮЮ ЗАВИСИМОСТЬ (#2917). Опасение «упадёт DaData — покраснеет
|
||||
# смоук без всякого регресса» проверено по коду и оказалось у́же, чем
|
||||
# звучит: `geocoder.suggest` — это цепочка «кадастровый тир → DaData →
|
||||
# Nominatim → []», и КАЖДЫЙ внешний тир обёрнут в `except Exception`
|
||||
# (services/geocoder.py). Отказ, квота и 5xx провайдера дают пустой список
|
||||
# и HTTP 200 — проверка остаётся зелёной. Покраснеть она может только если
|
||||
# провайдер ВИСНЕТ дольше 15 с (--max-time у curl), то есть на зависании,
|
||||
# а не на отказе. Ослаблять ожидание не стали: 200 здесь проверяет
|
||||
# открытость пути анониму, ради которой проверка и написана.
|
||||
check_post "meraocenka.ru public suggest — 200 anonymous" "$BASE_MERA/trade-in/api/public/mera/suggest" '{"q":"Малышева"}' 200
|
||||
check_post "meraocenka.ru public coverage — 200 anonymous" "$BASE_MERA/trade-in/api/public/mera/coverage" '{"lat":56.838,"lon":60.597,"rooms":2,"area_m2":54}' 200
|
||||
check "meraocenka.ru v1 geocode — must stay 404" "$BASE_MERA/trade-in/api/v1/geocode/suggest?q=test" 404
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue