137 lines
12 KiB
Markdown
137 lines
12 KiB
Markdown
# ТЗ — ORCH-36: Исполняемый самодеплой (стадия deploy дёргает хост-хук, Вариант B)
|
||
|
||
Work Item: ORCH-036
|
||
Stage: analysis
|
||
Автор: analyst
|
||
Дата: 2026-06-06
|
||
|
||
> Документ фиксирует ТРЕБОВАНИЯ к изменениям (что и где). Конкретный механизм
|
||
> (ssh vs docker.sock vs detached nohup/systemd-run; механизм approve) выбирает
|
||
> архитектор в ADR (`06-adr/`). ТЗ задаёт границы и контракты, не реализацию.
|
||
|
||
## 1. Текущее устройство (as-is, разведано в коде)
|
||
|
||
- **Стадии** (`src/stages.py`): `… testing → deploy-staging → deploy → done`.
|
||
- `deploy-staging`: `agent=deployer`, `qg=check_staging_status` (запускается deployer при
|
||
выходе из `deploy-staging`, входе в `deploy`).
|
||
- `deploy`: `agent=None`, `qg=check_deploy_status` (агент НЕ запускается при выходе из `deploy`).
|
||
- **Вывод:** реальную работу стадии `deploy` делает deployer-агент, запущенный на переходе
|
||
`deploy-staging → deploy`. Он пишет `14-deploy-log.md`. Когда он завершается, `advance_stage`
|
||
с `current_stage=deploy` прогоняет `check_deploy_status` и двигает `deploy → done`.
|
||
- **QG** (`src/qg/checks.py`):
|
||
- `check_deploy_status:464` → `_parse_deploy_status:406` читает ТОЛЬКО `deploy_status:` из
|
||
YAML-frontmatter `14-deploy-log.md` (worktree → origin/main fallback → not found).
|
||
- `check_staging_status:580` — условный (реален только для self-hosting `orchestrator`).
|
||
- `is_self_hosting_repo()` (`:511`) — детектор self-репо.
|
||
- **Откаты/диспетчеризация** (`src/stage_engine.py`):
|
||
- `_handle_qg_failure_rollbacks:585` — ветка `deployer` + `check_deploy_status` FAILED →
|
||
откат `deploy → development`, `set_issue_blocked`, release merge-lease, Plane+Telegram.
|
||
- Terminal-sync `deploy → done` (`:281`) → `set_issue_done`, release merge-lease.
|
||
- merge-gate (ORCH-43) на ребре `deploy-staging → deploy` — НЕ трогать.
|
||
- **Launcher** (`src/agents/launcher.py`):
|
||
- deployer-агент конфиг: `.task-deploy.md` / `.openclaw/agents/deployer.md` (`:180`).
|
||
- Пост-обработка: commit+push артефактов в worktree (`:506-558`).
|
||
- `exit_code != 0 && agent == deployer` → откат `deploy → development` (`:560-581`).
|
||
- **Хост-хук** (`scripts/orchestrator-deploy-hook.sh`, ORCH-34) — ГОТОВ: `--deploy`/`--rollback`,
|
||
параметризован env, дефолты STAGING; health 10×6с; авто-rollback; exit 0/1/2.
|
||
- **Agent (deployer.md)**: на стадии `deploy` сейчас пишет «бумажный» вердикт; в промпте маркер
|
||
«Real docker/SSH deploys are handled by scripts/orchestrator-deploy-hook.sh (ORCH-36)».
|
||
- **Топология** (`docs/operations/INFRA.md`): prod=8500 (`.env`), staging=8501 (`.env.staging`,
|
||
profile staging). Контейнер под uid 1000, доступ к docker.sock через gid 999.
|
||
|
||
## 2. Изменения по модулям (to-be)
|
||
|
||
### 2.1 `scripts/orchestrator-deploy-hook.sh` (донастройка прод-режима)
|
||
- Хук уже параметризован; требуется обеспечить **корректный прод-профиль вызова**:
|
||
`TARGET_SERVICE=orchestrator`, `TARGET_PORT=8500`, `TARGET_IMAGE=orchestrator-orchestrator`,
|
||
`COMPOSE_PROFILE` (для прод-сервиса — пустой/дефолтный, т.к. prod стартует без profile).
|
||
- **Build-once (BR-6):** деплой должен использовать образ, прошедший staging (перетег
|
||
staging-образа → прод-тег + `docker compose up -d --no-build`), а НЕ пересобирать. Если
|
||
текущий хук всегда `--no-build` и тянет `git pull` — уточнить в ADR, как гарантируется
|
||
идентичность артефакта staging↔prod (retag staging image, либо общий build-once шаг).
|
||
- `PREV_IMAGE_FILE` для прод — отдельный путь (например `.deploy-prev-image` без `-staging`),
|
||
чтобы не путать снапшоты prod/staging.
|
||
- Поведение `--rollback`, health-loop, exit-code (0=ok, 1=rolled back, 2=rollback тоже упал) —
|
||
НЕ менять контракт.
|
||
|
||
### 2.2 Approve-гейт (новое; место — на дизайне)
|
||
- Ввести флаг конфигурации `DEPLOY_REQUIRE_MANUAL_APPROVE` (bool, дефолт `true`).
|
||
- При `true`: перед вызовом прод-хука (после зелёного `deploy-staging`) конвейер ОСТАНАВЛИВАЕТСЯ
|
||
и ждёт явного «go» Владельца. Без «go» прод-хук НЕ вызывается.
|
||
- Механизм approve (выбрать ОДИН в ADR): Plane-коммент-триггер (по образцу `:approved:`
|
||
в `check_analysis_approved`) / Telegram-кнопка / signal-файл. Требование к механизму:
|
||
рестарт-safe (переживает перезапуск инстанса), идемпотентный, аудируемый.
|
||
- При `false` (вне этой задачи): approve-шаг пропускается — НЕ реализовывать выключение здесь,
|
||
только заложить ветку по флагу.
|
||
|
||
### 2.3 Триггер реального деплоя из стадии `deploy`
|
||
- На стадии `deploy` (для self-репо `orchestrator`) вместо/в дополнение к записи вердикта
|
||
агентом — ИНИЦИИРОВАТЬ внешний detached-процесс (host-хук), который выполнит
|
||
build-once+restart+health ПОСЛЕ выхода агента (BR-2: агент не рестартит сам себя).
|
||
- Маршрут вызова (на дизайне): ssh на хост (`DEPLOY_SSH_USER`/`DEPLOY_HOOK_SCRIPT`) ИЛИ
|
||
detached через docker.sock/nohup/systemd-run. Требование: процесс хука переживает выход
|
||
агента и завершение его сессии.
|
||
- Для **не-self** репо (enduro-trails): деплой по ssh на их хост (как раньше) — поведение не ломать.
|
||
|
||
### 2.4 Маппинг результата хука → `deploy_status`
|
||
- `deploy_status: SUCCESS` пишется в `14-deploy-log.md` ТОЛЬКО при exit-code хука = 0 (health-ok).
|
||
- exit-code ≠ 0 (1 = rolled back; 2 = rollback тоже упал) → `deploy_status: FAILED`.
|
||
- **Контракт `_parse_deploy_status` НЕ меняется** (читает `deploy_status: SUCCESS|FAILED` из
|
||
frontmatter). Меняется только КТО и КОГДА пишет этот вердикт — на основе реального исхода.
|
||
- **Гонка чтения гейта:** т.к. self-рестарт асинхронный (detached), гейт `check_deploy_status`
|
||
не должен прочитать вердикт ДО завершения хука. Механизм синхронизации (post-factum запись
|
||
лога/мердж в main / отложенный гейт) — спроектировать в ADR так, чтобы гейт читал РЕАЛЬНЫЙ
|
||
итог. Контракт чтения из worktree→origin/main (`_deploy_log_from_main`) можно переиспользовать.
|
||
|
||
### 2.5 Уведомления (BR-5)
|
||
- На промоут (старт прод-деплоя + успех) и на откат → `plane_add_comment(work_item_id, …)` +
|
||
`send_telegram(…)`. Переиспользовать существующие хелперы (`src/notifications.py`,
|
||
`src/plane_sync.py`). Никаких «молчаливых» деплоев.
|
||
|
||
### 2.6 Конфигурация (`src/config.py` / `.env.example` / `.env.staging.example`)
|
||
- Новый: `deploy_require_manual_approve: bool = True` (env `ORCH_DEPLOY_REQUIRE_MANUAL_APPROVE`).
|
||
- Прод-параметры хука: `DEPLOY_SSH_USER`, `DEPLOY_SSH_HOST`, `DEPLOY_HOOK_SCRIPT` (уже есть в
|
||
INFRA-карте) + прод-override `TARGET_SERVICE/PORT/IMAGE`. Прописать дескрипторы в `.env.example`
|
||
(значения — только на хосте, не коммитить).
|
||
- Условность по репо: реальный прод-деплой — только для self-hosting (`is_self_hosting_repo`),
|
||
как ORCH-35; прочие репо идут прежним ssh-путём.
|
||
|
||
### 2.7 Документация (BR-10, golden source)
|
||
- `.openclaw/agents/deployer.md` — раздел «Stage: deploy»: переписать с «бумажного SUCCESS» на
|
||
«стадия ВЫЗЫВАЕТ хук»; зафиксировать запрет синхронного рестарта 8500 и detached-путь self.
|
||
- `docs/operations/INFRA.md` — процедура прод-деплоя орка через хук + approve.
|
||
- `docs/operations/DEPLOY_HOOK.md` — обновить, если затронут контракт хука.
|
||
- `CHANGELOG.md` — запись о включении исполняемого деплоя (manual-approve).
|
||
- ADR в `docs/work-items/ORCH-036/06-adr/ADR-NNN-*.md` (создаёт архитектор).
|
||
|
||
## 3. API
|
||
- Изменений публичного HTTP API (`/health`, `/status`, `/queue`, `/webhook/*`) **не требуется**.
|
||
- Если approve реализуется через Plane-коммент — переиспользуется существующий webhook-путь
|
||
(`POST /webhook/plane`), новый endpoint не вводится. Если через signal-файл/Telegram —
|
||
внешний по отношению к HTTP API механизм. Решение — ADR.
|
||
|
||
## 4. Схема БД
|
||
- Изменения схемы **не требуются** для базового сценария (вердикт — в `14-deploy-log.md`;
|
||
approve-состояние желательно хранить рестарт-safe — допустимо через jobs/task_content или
|
||
signal-файл, без новой таблицы). Если архитектор сочтёт нужным поле статуса approve —
|
||
обосновать в ADR; по умолчанию — без миграции.
|
||
|
||
## 5. Требования к Quality Gates
|
||
- `check_deploy_status` и `_parse_deploy_status` — контракт чтения НЕ менять (frontmatter only).
|
||
- Откат `deploy → development` при `deploy_status: FAILED` (`stage_engine` БАГ-8) — сохранить.
|
||
- Terminal-sync `deploy → done` и release merge-lease — сохранить.
|
||
- merge-gate (`check_branch_mergeable`) на ребре `deploy-staging → deploy` — не затрагивать.
|
||
- `check_staging_status` остаётся обязательным предусловием (BR-7).
|
||
|
||
## 6. Артефакты pipeline
|
||
- Создаётся/обновляется: `docs/work-items/ORCH-036/14-deploy-log.md` (с РЕАЛЬНЫМ `deploy_status`).
|
||
- Обновляются по pipeline: `06-adr/ADR-NNN-*.md`, `12-review.md`, `13-test-report.md`,
|
||
`15-staging-log.md` (последующими агентами).
|
||
|
||
## 7. Нефункциональные требования
|
||
- **Безопасность self-deploy:** рестарт 8500 — только внешним рубильником; орк не может
|
||
необратимо убить себя.
|
||
- **Идемпотентность** хука и approve-механизма; **рестарт-safe** approve-состояние.
|
||
- **MTTR < 60с** при авто-rollback (health-loop хука 10×6с уже укладывается).
|
||
- **Отладка только на staging-цели** хука; реальный прод — лишь после approve.
|