From 073af241bed88bec9955d2bf3950441a690111f1 Mon Sep 17 00:00:00 2001 From: Stream Date: Thu, 4 Jun 2026 10:10:04 +0300 Subject: [PATCH] auto-sync: 2026-06-04 10:10:01 --- .../orchestrator/DEV_TASK_GITEA_MERGE_GATE.md | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 tasks/orchestrator/DEV_TASK_GITEA_MERGE_GATE.md diff --git a/tasks/orchestrator/DEV_TASK_GITEA_MERGE_GATE.md b/tasks/orchestrator/DEV_TASK_GITEA_MERGE_GATE.md new file mode 100644 index 0000000..da6833e --- /dev/null +++ b/tasks/orchestrator/DEV_TASK_GITEA_MERGE_GATE.md @@ -0,0 +1,85 @@ +# 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) +```python + 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` — это легитимный путь, когда фичу мержат не из конвейера). + +### Требуемая логика +```python + 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_request` merged — вероятно `tests/.../test_gitea_webhook*.py` или аналог). +- Кейсы: + 1. `closed+merged` при `current_stage="deploy"` → задача НЕ переходит в `done` + (остаётся на deploy), `update_task_stage(...,"done")` НЕ вызван. + 2. `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) +1. Перезапустить контейнер orchestrator (подхватить новый код). +2. Прогнать любую задачу до deploy ИЛИ юнит-проверкой убедиться: merge-вебхук на deploy + не ставит done; done ставится только после deployer verdict через check_deploy_status.