6.0 KiB
DEV TASK: закрыть вторую дверь баг-8 — gitea PR-merged обходит deploy-QG
Контекст / проблема
БАГ 8 (deploy без QG) был починен в stage_engine.advance_stage через check_deploy_status
(читает deploy_status: из frontmatter 14-deploy-log.md). Но есть вторая дверь:
обработчик Gitea-вебхука pull_request при action=closed & merged=true слепо переводит
задачу в done для ЛЮБОЙ стадии, минуя QG.
Боевой инцидент (ET-012, task 30):
- deployer мержит PR в начале работы → прилетает webhook
closed+merged→gitea.pyмгновенно ставитdone(за 33 сек), ПОКА deployer ещё работает 6 минут. - deployer затем пишет
deploy_status: FAILED— но поздно, задача ужеdone. check_deploy_statusне вызывается вообще → фейк-done вернулся через merge-вебхук.
Файл
src/webhooks/gitea.py — единственный файл с изменениями кода.
Текущий код (конец обработчика pull_request, примерно стр. 336-339)
elif action == "closed" and pr.get("merged", False):
update_task_stage(task_id, "done")
notify_stage_change(task_id, current_stage, "done")
logger.info(f"Task {task_id}: PR merged, stage → done")
Что нужно
Merge PR не должен напрямую ставить done, когда current_stage == "deploy".
Для deploy финальный переход deploy→done обязан проходить через check_deploy_status
(вердикт deployer'а), как и задумано баг-8 фиксом. Для всех ОСТАЛЬНЫХ стадий — поведение
merge сохранить как есть (прямой done — это легитимный путь, когда фичу мержат не из конвейера).
Требуемая логика
elif action == "closed" and pr.get("merged", False):
# БАГ 8 (вторая дверь): для стадии deploy финальный done гейтится
# вердиктом deployer'а (check_deploy_status), а НЕ фактом merge PR.
# deployer мержит PR в начале работы — merge != успешный деплой.
if current_stage == "deploy":
logger.info(
f"Task {task_id}: PR merged at deploy stage — done gated by "
f"deployer verdict (check_deploy_status), not merge event. Ignoring merge-driven done."
)
return
update_task_stage(task_id, "done")
notify_stage_change(task_id, current_stage, "done")
logger.info(f"Task {task_id}: PR merged, stage → done")
Обоснование игнора (а не вызова advance_stage): на deploy стадии deployer-job
уже запущен через очередь (enqueue_job на входе стадии). Когда он завершится,
launcher вызовет advance_stage, который выполнит check_deploy_status и сам решит
done/rollback. Дублировать advance_stage из merge-вебхука НЕ нужно — это создаст гонку.
Merge-вебхук на deploy просто молча игнорируется (done решает только вердикт).
ОГРАНИЧЕНИЯ (НЕ трогать)
- НЕ менять ветки
action == "reviewed"(APPROVED / REQUEST_CHANGES) — они работают. - НЕ менять HMAC, project-filter (
get_project_by_repo), импорты кроме необходимого. - НЕ трогать
stage_engine.py,launcher.py,checks.py,stages.py,conftest.py. - НЕ трогать nginx, openclaw.json, .env, queue, PLANE_STATES, gitea_public_url.
- Сохранить блок launcher.py:475.
Тесты
- Добавить тест(ы) в существующий тест-файл вебхуков gitea (найти где тестируется
pull_requestmerged — вероятноtests/.../test_gitea_webhook*.pyили аналог). - Кейсы:
closed+mergedприcurrent_stage="deploy"→ задача НЕ переходит вdone(остаётся на deploy),update_task_stage(...,"done")НЕ вызван.closed+mergedприcurrent_stageНЕ deploy (напримерreviewили иной валидный) → задача переходит вdone(регрессия не сломана).
- Запустить полный
pytest. Baseline: 227 passed + 10 failed (10 failed = off-limits: 9 HMAC/401 + 1 webhook-POST — их НЕ чинить, они и должны падать). Новые тесты должны дать 229+ passed, те же 10 failed.
Процесс сдачи
- Ветка от актуального main orchestrator:
fix/gitea-merge-deploy-gate. - Прямой push в main запрещён (pre-receive hook) → только через PR в Gitea.
- Gitea токен:
docker exec orchestrator printenv ORCH_GITEA_TOKEN. - Репо на проде:
/home/slin/repos/orchestrator(slin@82.22.50.71, пароль в .env). - В отчёте: commit-хеш, номер PR, вывод pytest (passed/failed), diff gitea.py.
- Отчёт положить в
tasks/orchestrator/reports/dev-2026-06-04-gitea-merge-gate.md.
Verify (после мержа PR)
- Перезапустить контейнер orchestrator (подхватить новый код).
- Прогнать любую задачу до deploy ИЛИ юнит-проверкой убедиться: merge-вебхук на deploy не ставит done; done ставится только после deployer verdict через check_deploy_status.