gendesign/.forgejo/workflows
bot-backend 6bb3189bc4
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI / openapi-codegen-check (pull_request) Successful in 2m10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Successful in 17m32s
chore(ci): деплой не убивает worker посреди скрап-прогона
Каждый деплой ПТИЦЫ безусловно пересоздавал worker — а он единственный, кто
несёт скрап-прогоны (beat только триггерит, отдельного сервиса scraper в
этом compose нет вовсе). Полный прогон КН идёт часами, при неудаче — сутки;
любой мерж в это окно убивал его молча, и деплой при этом был зелёным.

Заплатка до чекпоинтов (#3074): пока прогон не умеет продолжаться с места
обрыва, дешевле не обрывать.

Пересоздание worker'а пропускается, только когда выполнены оба условия:
образ действительно сменился (иначе пересоздание и так no-op) и прямо
сейчас есть живой прогон. Маркер заводить не пришлось, он уже есть —
kn_scrape_runs и objective_scrape_runs со status='running'; литералы сверены
с backend/app/workers/lifecycle.py, а не угаданы. Анти-зомби: считаются
только прогоны свежее WORKER_GUARD_MAX_SKIP_H часов по
COALESCE(heartbeat_at, started_at) — той же паре, что индексирует
data/sql/55_schema_scrape_resume.sql и по которой фильтрует reaper. Иначе
зависший навечно 'running' заморозил бы worker бесконечно.

Fail-safe перевёрнут в сторону статус-кво: psql молчит, отдал пустое, не
число, контейнера нет, image id не читается — пересоздаём как раньше и
печатаем WARNING. Тихий no-op деплоя опаснее убитого прогона: сломанный
детект иначе оставил бы worker на старом коде навсегда и незаметно.

Ревью в свежем контексте нашло критическую дыру, из-за которой заплатка не
работала бы ровно в своём целевом случае. Guard стоял ПОСЛЕ голого
`up -d` по всему стеку, а тот сам пересоздаёт любой сервис со сменившимся
образом. То есть worker убивался раньше, чем guard успевал сравнить digest'ы,
и к моменту проверки они уже совпадали — печаталось «образ не изменился», ни
SKIPPED, ни WARNING. Теперь worker явно исключён из bulk-команды: список
берётся из `config --services`, который фильтрует по активным профилям, так
что семантика сегодняшнего `up -d` сохраняется, а решение по worker'у
принимает только guard.

Там же поправлено: `tr -d '[:space:]'` вырезал пробел ВНУТРИ timestamp от
NOW(), из-за чего CAST падал и предупреждение «старый код + новая схема»
не печаталось бы никогда.

Риск заплатки назван прямо, потому что он реальный: пропуск означает, что
worker остаётся на старом образе до конца прогона. Миграции применяются ДО
подъёма приложения, поэтому старый код может оказаться на новой схеме —
на этот случай отдельная строка в логе с готовой командой ручного recreate,
и предупреждение о расхождении digest'ов повторяется в каждом следующем
деплое, пока расхождение живо.

Откат — переменная репозитория WORKER_RECREATE_GUARD в 'off', без коммита.

Проверено: YAML парсится, шелл-блок проходит bash -n, имена колонок сверены
со схемой (run_id, heartbeat_at в обеих таблицах, _schema_migrations.applied_at),
поведение `config --services` с профилями проверено на живом Docker —
сервисы под неактивными профилями в список не попадают, регрессии с #3061 нет.

Refs #3029, #3074
2026-08-24 20:06:00 +03:00
..
ci-tradein.yml fix(db/ci): чистый старт БД больше не падает на 077 и не может уехать на пустой схеме 2026-08-20 23:31:46 +03:00
ci.yml fix(ci): прод-деплои в одну группу concurrency — прун одного убивал pull другого (#2950) (#2952) 2026-08-20 07:47:48 +00:00
deploy-infra.yml feat(ci): деплой проверяет, к тому ли хосту подключился (#3079) 2026-08-24 16:36:50 +00:00
deploy-obsidian.yml feat(ci): деплой проверяет, к тому ли хосту подключился (#3079) 2026-08-24 16:36:50 +00:00
deploy-tradein.yml feat(ci): деплой проверяет, к тому ли хосту подключился (#3079) 2026-08-24 16:36:50 +00:00
deploy.yml chore(ci): деплой не убивает worker посреди скрап-прогона 2026-08-24 20:06:00 +03:00
perimeter-smoke.yml ci: смоук периметра МЕРЫ запускается сразу после деплоя (#2917) (#2922) 2026-08-19 08:13:49 +00:00
stale-claims.yml fix(stale-claims): avoid SIGPIPE 141 — pass file path не pipe 2026-05-28 01:05:56 +03:00